Mitiga il cross-site scripting (XSS) applicando un criterio di sicurezza del contenuto (CSP) rigoroso.

Lukas Weichselbaum
Lukas Weichselbaum

Supporto dei browser

  • Chrome: 52.
  • Edge: 79.
  • Firefox: 52.
  • Safari: 15.4.

Origine

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';
Come funziona un CSP rigoroso basato su nonce.

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.
di Gemini Advanced.

Adotta un criterio CSP rigoroso

Per adottare un criterio CSP rigoroso, devi:

  1. Decidi se la tua applicazione deve impostare un CSP basato su nonce o hash.
  2. Copia il CSP dalla sezione Rigorosa struttura CSP e impostalo come intestazione della risposta in tutta l'applicazione.
  3. Esegui il refactoring dei modelli HTML e del codice lato client per rimuovere i pattern che sono incompatibili con CSP.
  4. 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.

Faro
  segnala l&#39;avviso che non è stato trovato alcun CSP in modalità di applicazione forzata.
Se il tuo sito non ha un CSP, Lighthouse mostra questo avviso.

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.

Opzione A: CSP non basato su Ce

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:

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.

Opzione B: intestazione della risposta CSP basata su hash

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.

Un esempio di come incorporare i tuoi script.
Consentito da CSP
<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>
Per consentire l'esecuzione di questo script, devi calcolare l'hash dello script incorporato e aggiungerlo all'intestazione della risposta CSP, sostituendo {HASHED_INLINE_SCRIPT} segnaposto. Per ridurre la quantità di hash, puoi unire tutti i file incorporati in un unico script. Per vedere come funziona, consulta questo esempio e il relativo codice.
Bloccata da CSP
<script src="https://example.org/foo.js"></script>
<script src="https://example.org/bar.js"></script>
CSP blocca questi script perché possono essere sottoposti ad hashing solo gli script in linea.

Considerazioni sul caricamento degli script

Nell'esempio di script incorporato viene aggiunto s.async = falseper 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.

Report sulle violazioni dei CSP nella console per gli sviluppatori di Chrome.
Errori della console per codice bloccato.

Nella maggior parte dei casi, la correzione è semplice:

Esegui il refactoring dei gestori di eventi in linea

Consentito da CSP
<span id="things">A thing.</span>
<script nonce="${nonce}">
  document.getElementById('things').addEventListener('click', doThings);
</script>
CSP consente i gestori di eventi registrati mediante JavaScript.
Bloccata da CSP
<span onclick="doThings();">A thing.</span>
CSP blocca i gestori di eventi incorporati.

Esegui il refactoring di javascript: URI

Consentito da CSP
<a id="foo">foo</a>
<script nonce="${nonce}">
  document.getElementById('foo').addEventListener('click', linkClicked);
</script>
CSP consente i gestori di eventi registrati mediante JavaScript.
Bloccata da CSP
<a href="javascript:linkClicked()">foo</a>
CSP blocca JavaScript: URI.

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

Supporto dei browser

  • Chrome: 52.
  • Edge: 79.
  • Firefox: 52.
  • Safari: 15.4.

Origine

Se devi supportare versioni precedenti del browser:

  • Per utilizzare strict-dynamic è necessario aggiungere https: come riserva per le versioni precedenti versioni di Safari. Quando esegui questa operazione:
    • Tutti i browser che supportano strict-dynamic ignorano il metodo di riserva https:, 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:.
  • 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 ignorano unsafe-inline se è presente un nonce o un hash CSP.
di Gemini Advanced.
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:

  1. (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.
  2. 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).
di Gemini Advanced.

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 creano script 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 in eval(), 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