Cross-site scripting (XSS) di iniettare script dannosi in un'app web, è stata una delle le maggiori vulnerabilità di sicurezza web da oltre un decennio.
Criterio di sicurezza del contenuto (CSP)
è un ulteriore livello di sicurezza che aiuta a mitigare l'XSS. Per configurare un CSP:
aggiungi l'intestazione HTTP Content-Security-Policy
a una pagina web e imposta i valori che
controllare le risorse che lo user agent può caricare per la pagina.
In questa pagina viene spiegato come utilizzare un CSP basato su nonce o hash per mitigare l'XSS, anziché i CSP comunemente utilizzati basati su lista consentita basata su host che spesso escono dalla pagina esposti a XSS perché possono essere aggirati nella maggior parte delle configurazioni.
Termine chiave: un nonce è un numero casuale utilizzato solo una volta per contrassegnare un
Tag <script>
come attendibile.
Termine chiave: una funzione hash è una funzione matematica che converte un input
in un valore numerico compresso chiamato hash. Puoi usare un hash
(ad esempio, SHA-256) per contrassegnare una riga
Tag <script>
come attendibile.
Un criterio di sicurezza del contenuto basato su nonce o hash è spesso chiamato CSP rigoroso. Quando un'applicazione utilizza un CSP rigoroso, gli aggressori che trovano HTML difetti di iniezione in genere non possono utilizzarli per forzare l'esecuzione del browser script dannosi in un documento vulnerabile. Ciò è dovuto al fatto che solo CSP rigoroso consente script con hash o script con il valore nonce corretto generato nella in modo che gli utenti malintenzionati non possano eseguire lo script senza conoscere il nonce corretto per una determinata risposta.
Perché dovresti usare un CSP rigoroso?
Se il tuo sito ha già un CSP simile a script-src www.googleapis.com
,
probabilmente non è efficace
relativa ai cross-site. Questo tipo di CSP è chiamato
CSP nella lista consentita. Richiedono molta personalizzazione e possono essere
ignorata da utenti malintenzionati.
CSP rigorosi basati su nonce o hash crittografici evitano queste insidie.
Struttura CSP rigorosa
Un criterio di sicurezza del contenuto rigoroso di base utilizza una delle seguenti risposte HTTP intestazioni:
CSP rigoroso non basato su ce
Content-Security-Policy:
script-src 'nonce-{RANDOM}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
CSP rigoroso basato su hash
Content-Security-Policy:
script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
Le seguenti proprietà rendono "strito" un CSP come questo e quindi sicuri:
- Utilizza nonce
'nonce-{RANDOM}'
o hash'sha256-{HASHED_INLINE_SCRIPT}'
per indicare i tag<script>
che lo sviluppatore del sito ritiene affidabili per l'esecuzione il browser dell'utente. - Imposta
'strict-dynamic'
per ridurre l'impegno di deployment di un CSP basato su nonce o hash tramite consentendo l'esecuzione di script creati da uno script attendibile. Anche questo sblocca l'utilizzo della maggior parte delle librerie e dei widget JavaScript di terze parti. - Non si basa su liste consentite di URL, quindi non soffre bypass comuni di CSP.
- Blocca gli script incorporati non attendibili come i gestori di eventi in linea o
javascript:
per gli URI. - Limita
object-src
alla disattivazione di plug-in pericolosi come Flash. - Limita
base-uri
al blocco dell'inserimento di tag<base>
. In questo modo agli aggressori di modificare la posizione degli script caricati da URL relativi.
Adotta un criterio CSP rigoroso
Per adottare un criterio CSP rigoroso, devi:
- Decidi se la tua applicazione deve impostare un CSP basato su nonce o hash.
- Copia il CSP dalla sezione Rigorosa struttura CSP e impostalo come intestazione della risposta in tutta l'applicazione.
- Esegui il refactoring dei modelli HTML e del codice lato client per rimuovere i pattern che sono incompatibili con CSP.
- Esegui il deployment del CSP.
Puoi utilizzare Lighthouse
(v7.3.0 e successive con flag --preset=experimental
) Controllo best practice
durante la procedura per verificare se il tuo sito dispone di un CSP e se
abbastanza rigorosa per essere efficace
con l'XSS.
Passaggio 1: decidi se hai bisogno di un CSP basato su hash o nonce
Ecco come funzionano i due tipi di CSP rigoroso:
CSP nonce
Con un CSP nonce, generi un numero casuale in fase di runtime, lo includi il tuo CSP e associarlo a ogni tag script nella pagina. Un aggressore non può includere o eseguire uno script dannoso nella pagina, perché dovrebbe indovinare il numero casuale corretto per lo script. Funziona solo se il numero non è indovibile e viene generato in fase di runtime per ogni risposta.
Utilizza un CSP nonce per le pagine HTML visualizzate sul server. In queste pagine, puoi creare un nuovo numero casuale per ogni risposta.
CSP basato su hash
Per un CSP basato su hash, l'hash di ogni tag script incorporato viene aggiunto al CSP. Ogni script ha un hash diverso. Un aggressore non può includere o eseguire un'azione dannosa script nella pagina, perché l'hash di questo script dovrebbe essere presente CSP per l'esecuzione.
Utilizza un CSP basato su hash per le pagine HTML pubblicate in modo statico o per le pagine che devono essere memorizzati nella cache. Ad esempio, puoi utilizzare un CSP basato su hash per il web di una singola pagina di applicazioni create con framework come Angular, React o altri, che pubblicati in modo statico senza rendering lato server.
Passaggio 2: imposta un criterio CSP rigoroso e prepara gli script
Quando imposti un CSP, hai a disposizione alcune opzioni:
- Modalità di sola segnalazione (
Content-Security-Policy-Report-Only
) o modalità di applicazione forzata (Content-Security-Policy
). In modalità solo report, il CSP non blocca di risorse, quindi nessuna risorsa sul sito è inutilizzabile, ma puoi visualizzare errori e report per qualsiasi elemento che sarebbe stato bloccato. A livello locale, quando impostare il CSP, questo non ha importanza, perché entrambe le modalità mostrano nella console del browser. In caso affermativo, la modalità di applicazione forzata può aiutarti a trovare risorse bloccate dalla bozza di CSP, perché bloccare una risorsa può rendere la pagina sembra interrotta. La modalità solo report diventa più utile in una fase successiva del processo. (vedi il Passaggio 5). - Intestazione o tag
<meta>
HTML. Per lo sviluppo locale, un tag<meta>
può essere è molto utile per modificare il CSP e vedere rapidamente in che modo influisce sul tuo sito. Tuttavia:- In seguito, quando esegui il deployment del CSP in produzione, ti consigliamo di impostarlo come un'intestazione HTTP.
- Se vuoi impostare il CSP in modalità solo report, devi impostarlo come perché i meta tag CSP non supportano la modalità di solo report.
Imposta la seguente 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 ogni caricamento pagina. Un modello basato su nonce CSP può mitigare l'XSS solo se gli aggressori non sono in grado di indovinare il valore del nonce. R Il nonce CSP deve essere:
- Un valore casuale con una buona crittografia (idealmente di lunghezza superiore a 128 bit)
- Nuova generazione 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 criterio CSP nonce, ogni elemento <script>
deve
presentano un attributo nonce
che corrisponde al 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
CSP li consente.
Imposta la seguente 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 dinamicamente gli script di origine
Poiché gli hash CSP sono supportati nei browser solo per gli script in linea, devi caricare tutti gli script di terze parti in modo dinamico utilizzando uno script in linea. Gli hash per gli script di origine non sono ben supportati nei vari browser.
<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>
<script src="https://example.org/foo.js"></script> <script src="https://example.org/bar.js"></script>
Considerazioni sul caricamento degli script
Nell'esempio di script incorporato viene aggiunto s.async = false
per garantire
che foo
viene eseguito prima del giorno bar
, anche se
bar
viene caricato per primo. In questo snippet, s.async = false
non blocca l'analizzatore sintattico durante il caricamento degli script, in quanto gli script
aggiunti in modo dinamico. Il parser si interrompe solo durante l'esecuzione degli script,
che sarebbe per async
script. Tuttavia, con questo snippet
tieni presente che:
-
Uno o entrambi gli script possono essere eseguiti prima della fine del documento
download. Se vuoi che il documento sia pronto quando
script, attendi l'evento
DOMContentLoaded
prima aggiungi gli script. Se ciò causa un problema di prestazioni il download degli script non inizia abbastanza presto; usa i tag precaricati in precedenza nella pagina. -
defer = true
non fa nulla. Se necessario comportamento predefinito, esegui lo script manualmente quando necessario.
Passaggio 3: esegui il refactoring dei modelli HTML e del codice lato client
Gestori di eventi in linea (come onclick="…"
, onerror="…"
) e URI JavaScript
(<a href="javascript:…">
) può essere utilizzato per eseguire script. Ciò significa
che scopre che un bug XSS può iniettare questo tipo di codice HTML ed eseguire
JavaScript. Un CSP nonce o basato su hash vieta l'uso di questo tipo di markup.
Se il tuo sito utilizza uno di questi pattern, dovrai effettuare il refactoring per renderlo più sicuro
alternative.
Se hai attivato CSP nel passaggio precedente, potrai vedere le relative violazioni in ogni volta che CSP blocca un pattern incompatibile.
Nella maggior parte dei casi, la correzione è semplice:
Esegui il refactoring dei gestori di eventi in linea
<span id="things">A thing.</span> <script nonce="${nonce}"> document.getElementById('things').addEventListener('click', doThings); </script>
<span onclick="doThings();">A thing.</span>
Esegui il refactoring di javascript:
URI
<a id="foo">foo</a> <script nonce="${nonce}"> document.getElementById('foo').addEventListener('click', linkClicked); </script>
<a href="javascript:linkClicked()">foo</a>
Rimuovi eval()
da JavaScript
Se la tua applicazione utilizza eval()
per convertire le serializzazioni delle stringhe JSON in JS
è necessario eseguire il refactoring di tali istanze in JSON.parse()
, che è a sua volta
più velocemente.
Se non riesci a rimuovere tutti gli utilizzi di eval()
, puoi comunque impostare un criterio nonce rigoroso
ma devi usare la parola chiave CSP 'unsafe-eval'
, che rende i tuoi
sono leggermente meno sicuri.
Puoi trovare questi e altri esempi di refactoring di questo tipo in questo rigoroso ambito CSP. codelab:
(Facoltativo) Passaggio 4: aggiungi i fallback per supportare le versioni precedenti del browser
Se devi supportare versioni precedenti del browser:
- Per utilizzare
strict-dynamic
è necessario aggiungerehttps:
come riserva per le versioni precedenti versioni di Safari. Quando esegui questa operazione:- Tutti i browser che supportano
strict-dynamic
ignorano il metodo di riservahttps:
, Ciò non ridurrà l'efficacia del criterio. - Nei browser meno recenti, gli script provenienti da fonti esterne possono essere caricati solo se provengono da
un'origine HTTPS. È meno sicuro di un CSP rigoroso, ma è comunque
impedisce alcune cause XSS comuni, come l'inserimento di URI
javascript:
.
- Tutti i browser che supportano
- Per garantire la compatibilità con versioni del browser molto vecchie (più di 4 anni), puoi aggiungere
unsafe-inline
come opzione di riserva. Tutti i browser recenti ignoranounsafe-inline
se è 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 del CSP
Dopo aver verificato che il tuo CSP non blocca alcun script legittimo nel tuo locale di sviluppo, puoi eseguire il deployment del CSP in gestione temporanea, quindi nel dell'ambiente di produzione:
- (Facoltativo) Esegui il deployment del CSP in modalità solo report utilizzando
Intestazione
Content-Security-Policy-Report-Only
. La modalità di solo report è utile testare una modifica potenzialmente che provoca un errore, come un nuovo CSP in produzione, prima di iniziare ad applicare le limitazioni CSP. In modalità di solo report, il CSP non influiscono sul comportamento dell'app, ma il browser continua a generare errori della console e sulle violazioni, quando rileva pattern incompatibili con il CSP, per capire cosa avrebbe funzionato senza problemi per i tuoi utenti finali. Per maggiori informazioni informazioni, consulta la sezione API di reporting. - Se hai la certezza che il tuo CSP non comprometta il funzionamento del sito per gli utenti finali,
eseguire il deployment del CSP utilizzando l'intestazione della risposta
Content-Security-Policy
. Me utilizzare un'intestazione HTTP lato server, perché è più sicuro di un tag<meta>
. Dopo aver completato questo passaggio, il CSP inizia per proteggere la tua app da XSS (cross-site scripting).
Limitazioni
Un CSP rigoroso di solito fornisce un forte livello di sicurezza aggiuntivo che aiuta
per mitigare l'XSS. Nella maggior parte dei casi, CSP riduce significativamente la superficie di attacco,
rifiutando pattern pericolosi come gli URI javascript:
. Tuttavia, in base al tipo
di CSP che stai utilizzando (noce, hash, con o senza 'strict-dynamic'
), esistono
sono casi in cui CSP non protegge neanche la tua app:
- Se esegui il nonce uno script, ma viene eseguita un'iniezione direttamente nel corpo o nella
Parametro
src
dell'elemento<script>
. - In caso di inserimento nelle posizioni di script creati dinamicamente
(
document.createElement('script')
), anche in eventuali funzioni di libreria che creanoscript
nodi DOM in base ai valori dei relativi argomenti. Questo include alcune API comuni, come.html()
di jQuery, nonché.get()
e.post()
in jQuery < 3,0. - Se sono presenti iniezioni di modelli nelle vecchie applicazioni AngularJS. Un aggressore che può essere inserito in un modello AngularJS, puoi utilizzarlo eseguire JavaScript arbitrario.
- Se il criterio contiene
'unsafe-eval'
, viene inserito ineval()
,setTimeout()
e alcune altre API usate raramente.
Sviluppatori e tecnici della sicurezza dovrebbero prestare particolare attenzione a durante le revisioni del codice e i controlli di sicurezza. Puoi trovare ulteriori dettagli su casi in Criterio di sicurezza del contenuto: un pasticcio tra protezione avanzata e mitigazione.
Per approfondire
- CSP è morto, lunga vita CSP! sull'insicurezza delle whitelist e sul futuro delle norme di sicurezza dei contenuti
- Valutatore CSP
- Conferenza LocoMoco: Content Security Policy - Un pasticcio tra rafforzamento e mitigazione
- Discussione Google I/O: Protezione delle app web con funzionalità di piattaforma moderne