Browser Support
Il cross-site scripting (XSS), ovvero la possibilità di inserire script dannosi in un'app web, è una delle maggiori vulnerabilità della sicurezza web da oltre un decennio.
Content Security Policy (CSP)
è un livello di sicurezza aggiuntivo che contribuisce a mitigare gli attacchi XSS. Per configurare un 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 una CSP basata su nonce o hash per mitigare gli attacchi XSS, anziché le CSP basate su elenchi consentiti di host di uso comune, che spesso lasciano la pagina esposta agli attacchi XSS perché possono essere aggirate nella maggior parte delle configurazioni.
Termine chiave: un nonce è un numero casuale utilizzato una sola volta che puoi usare 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> in linea come attendibile.
Una Content Security Policy basata su nonce o hash viene spesso chiamata CSP rigida. Quando un'applicazione utilizza una CSP rigorosa, gli autori di attacchi che trovano difetti di iniezione HTML 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 autori di attacchi non possono eseguire lo script senza conoscere il nonce corretto per una determinata risposta.
Perché dovresti utilizzare una CSP rigorosa?
Se il tuo sito ha già una CSP simile a script-src www.googleapis.com,
probabilmente non è efficace contro gli attacchi cross-site. Questo tipo di CSP è chiamato
CSP con lista consentita. Richiedono molta personalizzazione e possono essere
aggirati dagli autori degli attacchi.
Le CSP rigide basate su nonce o hash crittografici evitano questi problemi.
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 'none39;;
base-uri 'none';
CSP rigoroso basato su hash
Content-Security-Policy:
script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
object-src 'none39;;
base-uri 'none';
Le seguenti proprietà rendono una CSP come questa "rigida" e quindi sicura:
- Utilizza nonce
'nonce-{RANDOM}'o hash'sha256-{HASHED_INLINE_SCRIPT}'per indicare di quali tag<script>lo sviluppatore del sito si fida per l'esecuzione nel browser dell'utente. - Imposta
'strict-dynamic'per ridurre lo sforzo di deployment di una CSP basata su nonce o hash consentendo automaticamente l'esecuzione di script creati da uno script attendibile. Inoltre, sblocca l'utilizzo della maggior parte dei widget e delle librerie JavaScript di terze parti. - Non si basa su elenchi consentiti di URL, quindi non soffre di bypass CSP comuni.
- Blocca gli script incorporati non attendibili, come i gestori di eventi incorporati o gli URI
javascript:. - Limita
object-srcper disattivare plug-in pericolosi come Flash. - Limita
base-uriper bloccare l'inserimento dei tag<base>. Ciò impedisce agli autori di attacchi di modificare le posizioni degli script caricati da URL relativi.
Adotta un CSP rigoroso
Per adottare una CSP rigorosa, devi:
- Decidi se la tua applicazione deve impostare una CSP basata su nonce o hash.
- Copia il CSP dalla sezione Struttura CSP rigorosa e impostalo come intestazione della risposta nell'applicazione.
- Esegui il refactoring dei modelli HTML e del codice lato client per rimuovere i pattern incompatibili con CSP.
- Esegui il deployment del CSP.
Puoi utilizzare il controllo Best Practices di Lighthouse
(versione 7.3.0 e successive con il flag --preset=experimental) durante questa procedura per verificare se il tuo sito ha una CSP e se è
abbastanza rigorosa 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 rigorosa:
CSP basata su nonce
Con un CSP basato su nonce, generi un numero casuale in fase di runtime, lo includi nel 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 quello script. Funziona solo se il numero non è intuibile e viene generato di nuovo in fase di runtime per ogni risposta.
Utilizza una CSP basata su nonce per le pagine HTML di cui è stato eseguito il rendering sul server. Per queste pagine, puoi creare un nuovo numero casuale per ogni risposta.
CSP basato su hash
Per una CSP basata su hash, l'hash di ogni tag di script incorporato viene aggiunto alla CSP. Ogni script ha un hash diverso. Un malintenzionato non può includere o eseguire uno script dannoso nella tua pagina, perché l'hash di questo script dovrebbe essere presente nella CSP per poter essere eseguito.
Utilizza una CSP basata su hash per le pagine HTML pubblicate in modo statico o per le pagine che devono essere memorizzate nella cache. Ad esempio, puoi utilizzare una CSP basata su hash per applicazioni web a pagina singola create con framework come Angular, React o altri, che vengono pubblicate staticamente senza rendering lato server.
Passaggio 2: imposta una CSP rigorosa e prepara gli script
Quando imposti un CSP, hai alcune opzioni:
- Modalità solo report (
Content-Security-Policy-Report-Only) o modalità di applicazione (Content-Security-Policy). In modalità solo report, la CSP non bloccherà ancora le risorse, quindi nulla sul tuo sito si interrompe, ma puoi visualizzare gli errori e ricevere report per qualsiasi elemento 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. Se non altro, la modalità di applicazione può aiutarti a trovare le risorse bloccate dalla bozza della CSP, perché il blocco di una risorsa può rendere la pagina danneggiata. La modalità solo report diventa più utile in un secondo momento della procedura (vedi Passaggio 5). - Tag
<meta>di intestazione o HTML. Per lo sviluppo locale, un tag<meta>può essere più comodo per modificare la CSP e vedere rapidamente come influisce sul tuo sito. Tuttavia:- In un secondo momento, quando esegui il deployment di CSP in produzione, ti consigliamo di impostarlo come intestazione HTTP.
- Se vuoi impostare la CSP in modalità solo report, devi impostarla come intestazione, perché i metatag 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';
Generare un nonce per CSP
Un nonce è un numero casuale utilizzato una sola volta per caricamento pagina. Una CSP basata su nonce può mitigare gli attacchi XSS solo se gli autori degli attacchi non riescono a indovinare il valore nonce. Un nonce CSP deve:
- Un valore casuale crittograficamente sicuro (idealmente di lunghezza pari o superiore a 128 bit)
- Generato di recente per ogni risposta
- Codificato in 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(&>quot;Content-Security-Policy", csp); // Every script tag in your application should set the `nonce` attribute to this value. response.render(template, { nonce: nonce }); });
Aggiungere un attributo nonce agli elementi <script>
Con una CSP basata 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}'.
Caricare gli script di origine in modo dinamico
Puoi caricare dinamicamente script di terze parti 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.hea<d.appen>dChild(s);
});
/script{HASHED_INLINE_SCRIPT}. Per ridurre il numero 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/fo><o.js&qu>o<t;/script script src="https://exam><ple.org>/bar.js"/script
integrity che corrisponde a un'origine consentita.
Considerazioni sul caricamento degli script
L'esempio di script incorporato aggiunge s.async = falseper garantire
che foo venga eseguito prima di bar, anche se
bar viene caricato per primo. In questo snippet, s.async = false
non blocca il parser durante il caricamento degli script, perché gli script vengono
aggiunti in modo dinamico. Il parser si arresta solo durante l'esecuzione degli script, come
farebbe 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 completato. Se vuoi che il documento sia pronto per l'esecuzione degli script, attendi l'evento
DOMContentLoadedprima di aggiungere gli script. Se ciò causa un problema di prestazioni perché gli script non iniziano il download abbastanza presto, utilizza i tag preload all'inizio della 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
malintenzionato che trova un bug XSS può inserire questo tipo di HTML ed eseguire codice
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 eseguirne il refactoring in alternative più sicure.
Se hai abilitato 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:
Rifattorizzare i gestori di eventi incorporati
<span id="th>ings&quo<t;A t>h<ing./span
script nonce=>"${nonce}"
document.getElementById('things').addEventL<istener>('click', doThings);
/script<span onclick="doThing>s();&quo<t;A t>hing./span
Refactoring degli URI javascript:
<a id=">;fo<o&>q<uot;foo/a
script nonce=>"${nonce}"
document.getElementById('foo').addEventList<ener(>39;click', linkClicked);
/script<a href="javascript:linkClick>ed(<)&>quot;foo/a
Rimuovi eval() da JavaScript
Se la tua applicazione utilizza eval() per convertire le serializzazioni di stringhe JSON in oggetti JS, devi eseguire il refactoring di 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 il criterio leggermente meno sicuro.
Puoi trovare questi e altri esempi di refactoring in questo codelab CSP rigido:
Passaggio 4 (facoltativo): aggiungi 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 fallback per le versioni precedenti di Safari. Quando esegui questa operazione:- Tutti i browser che supportano
strict-dynamicignorano il fallbackhttps:, quindi questo non ridurrà la validità delle norme. - Nei browser meno recenti, gli script di origine esterna possono essere caricati solo se provengono da
un'origine HTTPS. Questa opzione è meno sicura di una CSP rigorosa, 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 del browser molto precedenti (più di 4 anni), puoi aggiungere
unsafe-inlinecome fallback. 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 9;none';
base-uri 'none';
Passaggio 5: esegui il deployment del CSP
Dopo aver verificato che la CSP non blocca script legittimi nell'ambiente di sviluppo locale, puoi implementarla in staging e poi nell'ambiente di produzione:
- (Facoltativo) Implementa il CSP in modalità solo report utilizzando l'intestazione
Content-Security-Policy-Report-Only. La modalità solo report è utile per testare una modifica potenzialmente distruttiva, ad esempio una nuova CSP in produzione, prima di iniziare a applicare le restrizioni 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 da poter vedere cosa sarebbe andato in errore per gli utenti finali. Per saperne di più, consulta l'API Reporting. - Quando hai la certezza che la tua CSP non danneggerà il tuo sito per gli utenti finali,
implementa la 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, il CSP inizia a proteggere la tua app da XSS.
Limitazioni
Una CSP rigorosa in genere fornisce un livello aggiuntivo di sicurezza elevato che contribuisce a
mitigare gli attacchi XSS. Nella maggior parte dei casi, 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 altrettanto bene la tua app:
- Se aggiungi un nonce a uno script, ma viene eseguito un inserimento direttamente nel corpo o nel parametro
srcdell'elemento<script>. - Se sono presenti iniezioni nelle posizioni degli script creati dinamicamente
(
document.createElement('script')), incluse le funzioni di libreria che creano nodi DOMscriptin base ai valori dei relativi argomenti. Ciò include alcune API comuni come.html()di jQuery, nonché.get()e.post()in jQuery < 3.0. - Se sono presenti injection di modelli in vecchie applicazioni AngularJS. Un malintenzionato che può eseguire l'injection in un modello AngularJS può utilizzarlo per eseguire codice JavaScript arbitrario.
- Se la policy contiene
'unsafe-eval', iniezioni ineval(),setTimeout()e alcune altre API utilizzate raramente.
Gli sviluppatori e gli ingegneri della sicurezza devono prestare particolare attenzione a questi pattern durante le revisioni del codice e gli audit 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
- CSP Evaluator
- LocoMoco Conference: Content Security Policy - A successful mess between hardening and mitigation
- Presentazione di Google I/O: Proteggere le app web con le funzionalità moderne della piattaforma