Browser Support
Il cross-site scripting (XSS), la capacità di inserire script dannosi in un'app web, è stata una delle maggiori vulnerabilità della sicurezza web per oltre un decennio.
La Content Security Policy (CSP) è un livello di sicurezza aggiuntivo che contribuisce a mitigare gli attacchi XSS. Per configurare una CSP, aggiungi l'intestazione HTTP Content-Security-Policy a una pagina web e imposta i valori che controllano le risorse che lo user agent può caricare per quella pagina.
Questa pagina spiega come utilizzare un CSP basato su nonce o hash per mitigare gli XSS, anziché i CSP basati su liste consentite di host di uso comune che spesso lasciano la pagina esposta agli XSS perché possono essere bypassati nella maggior parte delle configurazioni.
Termine chiave: un nonce è un numero casuale utilizzato una sola volta che puoi utilizzare per contrassegnare un tag <script> come attendibile.
Termine chiave: una funzione hash è una funzione matematica che converte un valore di input in un valore numerico compresso chiamato hash. Puoi utilizzare un hash (ad esempio SHA-256) per contrassegnare un tag <script> incorporato come attendibile.
Una Content Security Policy basata su nonce o hash viene spesso chiamata CSP rigida. Quando un'applicazione utilizza una CSP rigida, gli utenti malintenzionati che trovano difetti di HTML injection in genere non possono utilizzarli per forzare il browser a eseguire script dannosi in un documento vulnerabile. Questo perché la CSP rigida consente solo script con hash o script con il valore nonce corretto generato sul server, quindi gli utenti malintenzionati non possono eseguire lo script senza conoscere il nonce corretto per una determinata risposta.
Perché dovresti utilizzare una CSP rigida?
Se il tuo sito ha già un CSP simile a script-src www.googleapis.com, probabilmente non è efficace contro il cross-site. Questo tipo di CSP è chiamato CSP con lista consentita. Richiedono molta personalizzazione e possono essere aggirati dagli utenti malintenzionati.
Le CSP rigide basate su nonce o hash crittografici evitano queste trappole.
Struttura CSP rigida
Una Content Security Policy di base rigorosa utilizza una delle seguenti intestazioni della risposta HTTP:
CSP rigida basata su nonce
Content-Security-Policy:
script-src 'nonce-{RANDOM}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
CSP rigida basata su hash
Content-Security-Policy:
script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
Le seguenti proprietà rendono un CSP come questo "rigido" e quindi sicuro:
- Utilizza nonce
'nonce-{RANDOM}'o hash'sha256-{HASHED_INLINE_SCRIPT}'per indicare quali tag<script>lo sviluppatore del sito ritiene attendibili per l'esecuzione nel browser dell'utente. - Imposta
'strict-dynamic'per ridurre l'impegno necessario per il deployment di un CSP basato su nonce o hash consentendo automaticamente l'esecuzione degli script creati da uno script attendibile. In questo modo, viene sbloccato anche l'utilizzo della maggior parte delle librerie e dei widget JavaScript di terze parti. - Non si basa su liste consentite di URL, quindi non è soggetto ai bypass CSP comuni.
- Blocca gli script incorporati non attendibili, come i gestori di eventi incorporati o gli URI
javascript:. - Limita
object-srcper disattivare i plug-in pericolosi come Flash. - Limita
base-uriper bloccare l'inserimento di tag<base>. In questo modo, gli utenti malintenzionati non possono modificare le posizioni degli script caricati da URL relativi.
Adotta una CSP rigida
Per adottare una CSP rigida, devi:
- Decidi se la tua applicazione deve impostare un CSP basato su nonce o hash.
- Copia il CSP dalla sezione Struttura CSP rigorosa e impostalo come intestazione della risposta nell'applicazione.
- Rifattorizza i modelli HTML e il codice lato client per rimuovere i pattern incompatibili con la CSP.
- Esegui il deployment del CSP.
Puoi utilizzare il controllo Best practice di Lighthouse (v7.3.0 e versioni successive con il flag --preset=experimental) durante questo processo per verificare se il tuo sito ha una CSP e se è abbastanza rigida da essere efficace contro XSS.
Passaggio 1: decidi se hai bisogno di una CSP basata su nonce o hash
Ecco come funzionano i due tipi di CSP rigida:
CSP basata su nonce
Con un CSP basato su nonce, generi un numero casuale in fase di runtime, lo includi nel tuo CSP e lo associ a ogni tag di script nella pagina. Un malintenzionato non può includere o eseguire uno script dannoso nella tua pagina, perché dovrebbe indovinare il numero casuale corretto per lo script. Questo funziona solo se il numero non è indovinabile e viene generato di nuovo in fase di runtime per ogni risposta.
Utilizza una CSP basata su nonce per le pagine HTML di cui viene eseguito il rendering sul server. Per queste pagine, puoi creare un nuovo numero casuale per ogni risposta.
CSP basata su hash
Per un CSP basato su hash, l'hash di ogni tag di script incorporato viene aggiunto al CSP. Ogni script ha un hash diverso. Un malintenzionato non può includere o eseguire uno script dannoso nella tua pagina, perché l'hash dello script dovrebbe essere presente nel CSP per poter essere eseguito.
Utilizza un CSP basato su hash per le pagine HTML pubblicate in modo statico o per le pagine che devono essere memorizzate nella cache. Ad esempio, puoi utilizzare un CSP basato su hash per le applicazioni web a pagina singola create con framework come Angular, React o altri, che vengono pubblicate in modo statico senza rendering lato server.
Passaggio 2: imposta un CSP rigido e prepara gli script
Quando imposti una CSP, hai a disposizione alcune opzioni:
- Modalità solo report (
Content-Security-Policy-Report-Only) o modalità di applicazione forzata (Content-Security-Policy). In modalità solo report, la CSP non blocca ancora le risorse, quindi non si rompe nulla sul tuo sito, ma puoi vedere gli errori e ricevere report per tutto ciò che sarebbe stato bloccato. A livello locale, quando imposti la CSP, questo non ha molta importanza, perché entrambe le modalità mostrano gli errori nella console del browser. In ogni caso, la modalità di applicazione forzata può aiutarti a trovare le risorse bloccate dalla bozza della CSP, perché il blocco di una risorsa può far sembrare la pagina danneggiata. La modalità Solo report diventa più utile in un secondo momento del processo (vedi il passaggio 5). - Intestazione o tag HTML
<meta>. Per lo sviluppo locale, un tag<meta>può essere più comodo per modificare la CSP e vedere rapidamente l'impatto sul sito. Tuttavia:- In seguito, quando esegui il deployment della CSP in produzione, ti consigliamo di impostarla come intestazione HTTP.
- Se vuoi impostare la CSP in modalità solo report, devi impostarla come intestazione, perché i meta tag CSP non supportano la modalità solo report.
Imposta la seguente intestazione della risposta HTTP Content-Security-Policy nella tua applicazione:
Content-Security-Policy: script-src 'nonce-{RANDOM}' 'strict-dynamic'; object-src 'none'; base-uri 'none';
Genera un nonce per CSP
Un nonce è un numero casuale utilizzato una sola volta per caricamento pagina. Un CSP basato su nonce può mitigare gli XSS solo se gli utenti malintenzionati non riescono a indovinare il valore nonce. Un nonce CSP deve essere:
- Un valore casuale crittograficamente sicuro (idealmente di lunghezza superiore a 128 bit)
- Generata di recente per ogni risposta
- Codifica Base64
Ecco alcuni esempi di come aggiungere un nonce CSP nei framework lato server:
- Django (Python)
- Express (JavaScript):
const app = express(); app.get('/', function(request, response) { // Generate a new random nonce value for every response. const nonce = crypto.randomBytes(16).toString("base64"); // Set the strict nonce-based CSP response header const csp = `script-src 'nonce-${nonce}' 'strict-dynamic'; object-src 'none'; base-uri 'none';`; response.set("Content-Security-Policy", csp); // Every <script> tag in your application should set the `nonce` attribute to this value. response.render(template, { nonce: nonce }); });
Aggiungi un attributo nonce agli elementi <script>
Con un CSP basato su nonce, ogni elemento <script> deve avere un attributo nonce che corrisponda al valore nonce casuale specificato nell'intestazione CSP. Tutti gli script possono avere lo stesso nonce. Il primo passaggio consiste nell'aggiungere questi attributi a tutti gli script in modo che la CSP li consenta.
Imposta la seguente intestazione della risposta HTTP Content-Security-Policy nella tua applicazione:
Content-Security-Policy: script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic'; object-src 'none'; base-uri 'none';
Per più script incorporati, la sintassi è la seguente:
'sha256-{HASHED_INLINE_SCRIPT_1}' 'sha256-{HASHED_INLINE_SCRIPT_2}'.
Carica gli script di origine in modo dinamico
Puoi caricare script di terze parti in modo dinamico utilizzando uno script incorporato.
<script>
var scripts = [ 'https://example.org/foo.js', 'https://example.org/bar.js'];
scripts.forEach(function(scriptUrl) {
var s = document.createElement('script');
s.src = scriptUrl;
s.async = false; // to preserve execution order
document.head.appendChild(s);
});
</script>{HASHED_INLINE_SCRIPT}. Per ridurre la quantità di hash, puoi unire tutti gli script incorporati in un unico script. Per vedere un esempio pratico, consulta questo esempio e il relativo codice.
<script src="https://example.org/foo.js"></script> <script src="https://example.org/bar.js"></script>
integrity che corrisponde a un'origine consentita.
Considerazioni sul caricamento degli script
L'esempio di script incorporato aggiunge s.async = false per garantire che foo venga eseguito prima di bar, anche se bar viene caricato per primo. In questo snippet, s.async = false non blocca l'analizzatore sintattico durante il caricamento degli script, perché questi vengono aggiunti dinamicamente. Il parser si interrompe solo durante l'esecuzione degli script, come avviene per gli script async. Tuttavia, con questo snippet, tieni presente quanto segue:
-
Uno o entrambi gli script potrebbero essere eseguiti prima che il download del documento sia stato completato. Se vuoi che il documento sia pronto al momento dell'esecuzione degli script, attendi l'evento
DOMContentLoadedprima di aggiungere gli script. Se ciò causa un problema di prestazioni perché il download degli script non inizia abbastanza presto, utilizza i tag di precaricamento in precedenza nella pagina. -
defer = truenon fa nulla. Se hai bisogno di questo comportamento, esegui lo script manualmente quando necessario.
Passaggio 3: rifattorizza i modelli HTML e il codice lato client
I gestori di eventi incorporati (come onclick="…", onerror="…") e gli URI JavaScript (<a href="javascript:…">) possono essere utilizzati per eseguire script. Ciò significa che un utente malintenzionato che trova un bug XSS può inserire questo tipo di HTML ed eseguire JavaScript dannoso. Una CSP basata su nonce o hash vieta l'utilizzo di questo tipo di markup.
Se il tuo sito utilizza uno di questi pattern, dovrai rifattorizzarli in alternative più sicure.
Se hai attivato CSP nel passaggio precedente, potrai visualizzare le violazioni di CSP nella console ogni volta che CSP blocca un pattern incompatibile.
Nella maggior parte dei casi, la correzione è semplice:
Rifattorizza i gestori di eventi incorporati
<span id="things">A thing.</span>
<script nonce="${nonce}">
document.getElementById('things').addEventListener('click', doThings);
</script><span onclick="doThings();">A thing.</span>
Rifattorizza gli URI javascript:
<a id="foo">foo</a>
<script nonce="${nonce}">
document.getElementById('foo').addEventListener('click', linkClicked);
</script><a href="javascript:linkClicked()">foo</a>
Rimuovi eval() dal codice JavaScript
Se la tua applicazione utilizza eval() per convertire le serializzazioni di stringhe JSON in oggetti JS, devi rifattorizzare queste istanze in JSON.parse(), che è anche più veloce.
Se non riesci a rimuovere tutti gli utilizzi di eval(), puoi comunque impostare un CSP rigoroso basato su nonce, ma devi utilizzare la parola chiave CSP 'unsafe-eval', che rende la policy leggermente meno sicura.
Puoi trovare questi e altri esempi di questo tipo di refactoring in questo codelab sulla CSP rigida:
(Facoltativo) Passaggio 4: aggiungi i fallback per supportare le versioni precedenti del browser
Browser Support
Se devi supportare versioni precedenti del browser:
- L'utilizzo di
strict-dynamicrichiede l'aggiunta dihttps:come riserva per le versioni precedenti di Safari. Quando esegui questa operazione:- Tutti i browser che supportano
strict-dynamicignorano il valore di riservahttps:, quindi questo non ridurrà l'efficacia della policy. - Nei browser meno recenti, gli script di origine esterna possono essere caricati solo se provengono da un'origine HTTPS. Questa opzione è meno sicura di un CSP rigoroso, ma impedisce comunque alcune cause comuni di XSS, come le iniezioni di URI
javascript:.
- Tutti i browser che supportano
- Per garantire la compatibilità con versioni molto vecchie del browser (più di 4 anni), puoi aggiungere
unsafe-inlinecome opzione di riserva. Tutti i browser recenti ignoranounsafe-inlinese è presente un nonce o un hash CSP.
Content-Security-Policy:
script-src 'nonce-{random}' 'strict-dynamic' https: 'unsafe-inline';
object-src 'none';
base-uri 'none';
Passaggio 5: esegui il deployment della CSP
Dopo aver verificato che la CSP non blocca gli script legittimi nel tuo ambiente di sviluppo locale, puoi eseguirne il deployment in gestione temporanea e poi nell'ambiente di produzione:
- (Facoltativo) Esegui il deployment della CSP in modalità solo report utilizzando l'intestazione
Content-Security-Policy-Report-Only. La modalità Solo report è utile per testare una modifica che provoca un errore, come un nuovo CSP in produzione, prima di iniziare ad applicare le limitazioni del CSP. In modalità Solo report, la CSP non influisce sul comportamento dell'app, ma il browser genera comunque errori della console e report sulle violazioni quando rileva pattern incompatibili con la CSP, in modo che tu possa vedere cosa non avrebbe funzionato per gli utenti finali. Per saperne di più, consulta l'API Reporting. - Quando hai la certezza che la CSP non interromperà il sito per gli utenti finali, esegui il deployment della CSP utilizzando l'intestazione della risposta
Content-Security-Policy. Ti consigliamo di impostare la CSP utilizzando un'intestazione HTTP lato server, perché è più sicura di un tag<meta>. Dopo aver completato questo passaggio, la CSP inizia a proteggere la tua app dagli attacchi XSS.
Limitazioni
In genere, una CSP rigida fornisce un ulteriore livello di sicurezza efficace che contribuisce a mitigare gli attacchi XSS. Nella maggior parte dei casi, il CSP riduce notevolmente la superficie di attacco rifiutando pattern pericolosi come gli URI javascript:. Tuttavia, in base al tipo di CSP che utilizzi (nonce, hash, con o senza 'strict-dynamic'), esistono casi in cui la CSP non protegge l'app:
- Se utilizzi un nonce per uno script, ma è presente un'injection direttamente nel corpo o nel parametro
srcdell'elemento<script>. - Se sono presenti injection nelle posizioni degli script creati dinamicamente (
document.createElement('script')), incluse le funzioni di libreria che creano nodi DOMscriptin base ai valori dei relativi argomenti. Sono incluse alcune API comuni come.html()di jQuery, nonché.get()e.post()in jQuery < 3.0. - Se sono presenti template injection nelle vecchie applicazioni AngularJS. Un malintenzionato che può eseguire l'injection in un modello AngularJS può utilizzarlo per eseguire JavaScript arbitrario.
- Se la policy contiene
'unsafe-eval', le iniezioni ineval(),setTimeout()e alcune altre API utilizzate raramente.
Gli sviluppatori e gli ingegneri della sicurezza dovrebbero prestare particolare attenzione a questi pattern durante le revisioni del codice e i controlli di sicurezza. Puoi trovare maggiori dettagli su questi casi in Content Security Policy: A Successful Mess Between Hardening and Mitigation.
Per approfondire
- CSP Is Dead, Long Live CSP! On the Insecurity of Whitelists and the Future of Content Security Policy
- Valutatore CSP
- LocoMoco Conference: Content Security Policy - A successful mess between hardening and mitigation
- Talk di Google I/O: Protezione delle app web con le moderne funzionalità della piattaforma