I criteri di sicurezza dei contenuti possono ridurre notevolmente il rischio e l'impatto degli attacchi cross-site scripting nei browser moderni.
Il modello di sicurezza del web si basa su un
criterio di origine stessa. Ad esempio, il codice di https://mybank.com
deve avere accesso solo ai dati di https://mybank.com
e non deve mai essere consentito l'accesso a https://evil.example.com
.
In teoria, ogni origine viene mantenuta isolata dal resto del web, offrendo agli sviluppatori una sandbox sicura in cui eseguire l'integrazione. In pratica, tuttavia, gli hacker hanno
trovato diversi modi per sovvertire il sistema.
Ad esempio, gli attacchi di cross-site scripting (XSS) aggirano il criterio di origine stessa ingannando un sito affinché invii codice dannoso insieme ai contenuti previsti. Si tratta di un problema enorme, poiché i browser ritengono attendibile tutto il codice visualizzato in una pagina come parte legittima dell'origine della sicurezza della pagina. La cheat sheet XSS è una vecchia, ma rappresentativa, sezione trasversale dei metodi che un malintenzionato potrebbe utilizzare per violare questa attendibilità iniettando codice dannoso. Se un malintenzionato riesce a inserire qualsiasi codice, ha compromesso la sessione utente e ha ottenuto l'accesso a informazioni private.
Questa pagina illustra i criteri di sicurezza del contenuto (CSP) come strategia per ridurre il rischio e l'impatto degli attacchi XSS nei browser moderni.
Componenti di CSP
Per implementare un CSP efficace, svolgi i seguenti passaggi:
- Utilizza le liste consentite per indicare al cliente cosa è consentito e cosa no.
- Scopri quali direttive sono disponibili.
- Scopri le parole chiave che accettano.
- Limita l'utilizzo del codice in linea e di
eval()
. - Segnala le violazioni delle norme al tuo server prima di applicarle.
Elenchi consentiti di origini
Gli attacchi XSS sfruttano l'incapacità del browser di distinguere tra gli script che fanno parte della tua applicazione e quelli iniettati in modo dannoso da terze parti. Ad esempio, il pulsante Google +1 in fondo a questa pagina carica ed esegue il codice di https://apis.google.com/js/plusone.js
nel contesto dell'origine di questa pagina.
Ci fidiamo di questo codice, ma non possiamo aspettarci che il browser capisca autonomamente
che il codice di apis.google.com
è sicuro da eseguire, mentre il codice di
apis.evil.example.com
probabilmente non lo è. Il browser scarica e
esegue qualsiasi codice richiesto da una pagina, indipendentemente dall'origine.
L'intestazione HTTP Content-Security-Policy
di CSP ti consente di creare una lista consentita di fonti di contenuti attendibili e indica al browser di eseguire o visualizzare solo le risorse provenienti da queste origini. Anche se un utente malintenzionato riesce a trovare una falla per iniettare uno script, lo script non corrisponderà alla lista consentita e, pertanto, non verrà eseguito.
Ci fidiamo di apis.google.com
per la consegna di codice valido e ci fidiamo di noi stessi per fare lo stesso. Di seguito è riportato un esempio di criterio che consente l'esecuzione degli script solo
se provengono da una di queste due origini:
Content-Security-Policy: script-src 'self' https://apis.google.com
script-src
è una direttiva che controlla un insieme di privilegi relativi agli script per una pagina. Questa intestazione 'self'
come una fonte di script valida e
https://apis.google.com
come un'altra. Ora il browser può scaricare ed eseguire JavaScript da apis.google.com
tramite HTTPS, nonché dall'origine della pagina corrente, ma non da altre origini. Se un malintenzionato inietta codice nel tuo sito, il browser genera un errore e non esegue lo script iniettato.
I criteri si applicano a una vasta gamma di risorse
CSP fornisce un insieme di istruzioni relative ai criteri che consentono un controllo granulare sulle risorse che è consentito caricare su una pagina, tra cui script-src
dell'esempio precedente.
L'elenco seguente illustra le altre istruzioni relative alle risorse a partire dal livello 2. È stata redatta una specifica di livello 3, ma non è stata implementata per la maggior parte nei browser principali.
base-uri
- Limita gli URL che possono essere visualizzati nell'elemento
<base>
di una pagina. child-src
- Elenca gli URL dei lavoratori e dei contenuti del frame incorporato. Ad esempio,
child-src https://youtube.com
consente di incorporare i video di YouTube, ma non quelli di altre origini. connect-src
- Limita le origini a cui puoi connetterti utilizzando XHR, WebSockets ed EventSource.
font-src
- Specifica le origini che possono pubblicare caratteri web. Ad esempio, puoi consentire i caratteri web di Google utilizzando
font-src https://themes.googleusercontent.com
. form-action
- Elenca gli endpoint validi per l'invio dai tag
<form>
. frame-ancestors
- Specifica le origini che possono incorporare la pagina corrente. Questa direttiva si applica ai tag
<frame>
,<iframe>
,<embed>
e<applet>
. Non può essere utilizzato nei tag<meta>
o per le risorse HTML. frame-src
- Questa istruzione è stata deprecata nel livello 2, ma è stata ripristinata nel livello 3. Se non è presente, il browser utilizza
child-src
. img-src
- Definisce le origini da cui è possibile caricare le immagini.
media-src
- Limita le origini consentite per la pubblicazione di video e audio.
object-src
- Consente il controllo di Flash e di altri plug-in.
plugin-types
- Limita i tipi di plug-in che una pagina può richiamare.
report-uri
- Specifica un URL a cui il browser invia i report quando viene violato un criterio di sicurezza dei contenuti. Questa direttiva non può essere utilizzata nei tag
<meta>
. style-src
- Limita le origini da cui una pagina può utilizzare i fogli di stile.
upgrade-insecure-requests
- Indica agli user agent di riscrivere gli schemi degli URL passando da HTTP a HTTPS. Questa direttiva è destinata ai siti web con un numero elevato di URL precedenti che devono essere riscritti.
worker-src
- Una direttiva CSP di Livello 3 che limita gli URL che possono essere caricati come worker, worker condiviso o service worker. Da luglio 2017, questa direttiva ha implementazioni limitate.
Per impostazione predefinita, il browser carica la risorsa associata da qualsiasi origine, senza limitazioni, a meno che non imposti un criterio con un'istruzione specifica. Per eseguire l'override
del valore predefinito, specifica un'istruzione default-src
. Questa istruzione definisce i valori predefiniti per qualsiasi istruzione non specificata che termina con -src
. Ad esempio, se imposti default-src
su https://example.com
e non specifichi un'istruzione font-src
, puoi caricare caratteri solo da https://example.com
.
Le seguenti direttive non utilizzano default-src
come opzione di riserva. Ricorda che se non li imposti, è come se consentissi tutto:
base-uri
form-action
frame-ancestors
plugin-types
report-uri
sandbox
Sintassi di base del CSP
Per utilizzare le direttive CSP, elencale nell'intestazione HTTP con le direttive separate da due punti. Assicurati di elencare tutte le risorse richieste di un tipo specifico in una singola istruzione, come segue:
script-src https://host1.com https://host2.com
Di seguito è riportato un esempio di più istruzioni, in questo caso per un'app web che carica tutte le sue risorse da una rete CDN (Content Delivery Network) all'indirizzo https://cdn.example.net
e non utilizza contenuti all'interno di frame o plug-in:
Content-Security-Policy: default-src https://cdn.example.net; child-src 'none'; object-src 'none'
Dettagli di implementazione
I browser moderni supportano l'intestazione Content-Security-Policy
senza prefisso.
Questa è l'intestazione consigliata. Le intestazioni X-WebKit-CSP
e
X-Content-Security-Policy
che potresti vedere nei tutorial online
sono deprecate.
Il CSP è definito su base pagina. Dovrai inviare l'intestazione HTTP con ogni risposta che vuoi proteggere. In questo modo puoi ottimizzare i criteri per pagine specifiche in base alle loro esigenze specifiche. Ad esempio, se un insieme di pagine nel tuo sito ha un pulsante +1, mentre altre no, puoi consentire il caricamento del codice del pulsante solo quando necessario.
L'elenco delle origini per ogni direttiva è flessibile. Puoi specificare le origini in base allo schema (data:
, https:
) o in base a una specificità che va da solo nome host (example.com
, che corrisponde a qualsiasi origine su quell'host: qualsiasi schema, qualsiasi porta) a un URI completamente qualificato (https://example.com:443
, che corrisponde solo a HTTPS, soloexample.com
e solo alla porta 443). I caratteri jolly sono accettati, ma solo come schema,
una porta o nella posizione più a sinistra del nome host: *://*.example.com:*
corrisponderebbe
a tutti i sottodomini di example.com
(ma non al valore example.com
stesso), utilizzando
qualsiasi schema, su qualsiasi porta.
L'elenco delle origini accetta anche quattro parole chiave:
'none'
non corrisponde a nulla.'self'
corrisponde all'origine corrente, ma non ai relativi sottodomini.'unsafe-inline'
consente JavaScript e CSS in linea. Per maggiori informazioni, consulta la sezione Evitare il codice in linea.'unsafe-eval'
consente meccanismi di conversione di testo in JavaScript comeeval
. Per ulteriori informazioni, consulta la sezione Evitareeval()
.
Queste parole chiave richiedono virgolette singole. Ad esempio, script-src 'self'
(con virgolette) consente l'esecuzione di JavaScript dall'host corrente; script-src self
(senza virgolette) consente JavaScript da un server denominato "self
" (e non dall'host corrente), che probabilmente non è ciò che intendevi.
Esegui la sandbox delle pagine
C'è un'altra direttiva che vale la pena di menzionare: sandbox
. È un po' diversa dalle altre che abbiamo esaminato, in quanto impone limitazioni alle azioni che la pagina può intraprendere anziché alle risorse che può caricare. Se è presente l'istruzione sandbox
, la pagina viene trattata come se fosse stata caricata all'interno di un <iframe>
con un attributo sandbox
. Ciò può avere una vasta gamma di effetti sulla pagina: forzare la pagina in un'origine univoca e impedire l'invio del modulo, tra gli altri. Non rientra nell'ambito di questa pagina, ma
puoi trovare informazioni dettagliate sugli attributi di sandbox validi nella
sezione "Sandbox" della specifica HTML5.
Il meta tag
Il meccanismo di inserimento preferito dai CSP è un'intestazione HTTP. Tuttavia, può essere utile impostare un criterio su una pagina direttamente nel markup. A tale scopo, utilizza un tag <meta>
con un attributo http-equiv
:
<meta http-equiv="Content-Security-Policy" content="default-src https://cdn.example.net; child-src 'none'; object-src 'none'">
Non può essere utilizzato per frame-ancestors
, report-uri
o sandbox
.
Evita il codice in linea
Per quanto efficaci siano le liste consentite basate sull'origine utilizzate nelle direttive CSP,
non possono risolvere la più grande minaccia rappresentata dagli attacchi XSS: l'iniezione di script in linea.
Se un malintenzionato riesce a iniettare un tag script che contiene direttamente un payload dannoso (ad esempio <script>sendMyDataToEvilDotCom()</script>
), il browser non ha modo di distinguerlo da un tag script in linea legittimo. Il CSP risolve questo
problema vietando completamente gli script in linea.
Questo divieto include non solo gli script incorporati direttamente nei tag script
, ma anche gli URL javascript:
e i gestori degli eventi in linea. Dovrai spostare i contenuti dei tag script
in un file esterno e sostituire gli URL javascript:
e <a ...
onclick="[JAVASCRIPT]">
con chiamate addEventListener()
appropriate. Ad esempio,
puoi riscrivere quanto segue:
<script>
function doAmazingThings() {
alert('YOU ARE AMAZING!');
}
</script>
<button onclick='doAmazingThings();'>Am I amazing?</button>
a qualcosa di più simile a:
<!-- amazing.html -->
<script src='amazing.js'></script>
<button id='amazing'>Am I amazing?</button>
// amazing.js
function doAmazingThings() {
alert('YOU ARE AMAZING!');
}
document.addEventListener('DOMContentLoaded', function () {
document.getElementById('amazing')
.addEventListener('click', doAmazingThings);
});
Il codice riscritto non è solo compatibile con CSP, ma è anche allineato con le best practice di web design. Il codice JavaScript in linea mescola struttura e comportamento in modi che rendono il codice confuso. Inoltre, è più complicato memorizzare nella cache e compilare. Spostare il codice in risorse esterne migliora il rendimento delle pagine.
Inoltre, ti consigliamo vivamente di spostare attributi e tag style
incorporati in fogli di stile esterni per proteggere il tuo sito da attacchi di esfiltrazione di dati basati su CSS.
Come consentire temporaneamente script e stili in linea
Puoi attivare gli script e gli stili in linea aggiungendo 'unsafe-inline'
come origine consentita in una direttiva script-src
o style-src
. Il livello 2 del CSP ti consente inoltre di aggiungere script in linea specifici alla lista consentita utilizzando un nonce crittografico (numero utilizzato una volta) o un hash come segue.
Per utilizzare un nonce, assegna al tag script un attributo nonce. Il valore deve corrispondere a uno nell'elenco delle origini attendibili. Ad esempio:
<script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
// Some inline code I can't remove yet, but need to as soon as possible.
</script>
Aggiungi il nonce all'istruzione script-src
dopo la parola chiave nonce-
:
Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'
I token nonce devono essere rigenerati per ogni richiesta di pagina e non devono essere deducibili.
Gli hash funzionano in modo simile. Anziché aggiungere codice al tag script, crea
un hash SHA dello script stesso e aggiungilo all'istruzione script-src
.
Ad esempio, se la tua pagina conteneva quanto segue:
<script>alert('Hello, world.');</script>
Le norme devono contenere quanto segue:
Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng='
Il prefisso sha*-
specifica l'algoritmo che genera l'hash. L'esempio precedente utilizza sha256-
, ma CSP supporta anche sha384-
e sha512-
. Quando
generi l'hash, ometti i tag <script>
. Le lettere maiuscole e gli spazi vuoti
sono importanti, inclusi gli spazi vuoti iniziali e finali.
Le soluzioni per la generazione di hash SHA sono disponibili in qualsiasi lingua. Se utilizzi Chrome 40 o versioni successive, puoi aprire DevTools e ricaricare la pagina. La scheda Console mostra i messaggi di errore con l'hash SHA-256 corretto per ogni script inline.
Evita eval()
Anche se un malintenzionato non può iniettare direttamente lo script, potrebbe riuscire a ingannare la tua applicazione per farle convertire il testo inserito in JavaScript eseguirlo per suo conto. eval()
, new Function()
,
setTimeout([string], …)
e setInterval([string], ...)
sono tutti vettori
che gli attaccanti possono utilizzare per eseguire codice dannoso tramite il testo iniettato. La risposta predefinita del CSP a questo rischio è bloccare completamente tutti questi vettori.
Ciò ha i seguenti effetti sul modo in cui crei le applicazioni:
- Devi analizzare il JSON utilizzando
JSON.parse
integrato, anziché fare affidamento sueval
. Le operazioni JSON sicure sono disponibili in tutti i browser a partire da IE8. Devi riscrivere le chiamate
setTimeout
osetInterval
che effettui utilizzando funzioni in linea anziché stringhe. Ad esempio, se la tua pagina contiene quanto segue:setTimeout("document.querySelector('a').style.display = 'none';", 10);
Riscrivilo come:
setTimeout(function () { document.querySelector('a').style.display = 'none'; }, 10); ```
Evita i modelli incorporati in fase di runtime. Molte librerie di modelli utilizzano spesso
new Function()
per velocizzare la generazione dei modelli in fase di esecuzione, il che consente la valutazione del testo dannoso. Alcuni framework supportano CSP out of the box, ricorrendo a un parser robusto in assenza dieval
. La direttiva ng-csp di AngularJS è un buon esempio. Tuttavia, consigliamo di utilizzare un linguaggio di creazione di modelli che offra la precompilazione, ad esempio Handlebars. La precompilazione dei modelli può rendere l'esperienza utente ancora più veloce rispetto all'implementazione in fase di esecuzione più rapida, oltre a rendere il tuo sito più sicuro.
Se eval()
o altre funzioni di conversione di testo in JavaScript sono essenziali per la tua applicazione, puoi attivarle aggiungendo 'unsafe-eval'
come origine consentita in una direttiva script-src
. Sconsigliamo vivamente di farlo a causa del rischio
di iniezione di codice che presenta.
Segnalare violazioni delle norme
Per notificare al server i bug che potrebbero consentire l'iniezione di codice dannoso, puoi chiedere al browser di POST
inviare le segnalazioni di violazioni in formato JSON a una posizione specificata in una direttiva report-uri
:
Content-Security-Policy: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;
Questi report hanno il seguente aspetto:
{
"csp-report": {
"document-uri": "http://example.org/page.html",
"referrer": "http://evil.example.com/",
"blocked-uri": "http://evil.example.com/evil.js",
"violated-directive": "script-src 'self' https://apis.google.com",
"original-policy": "script-src 'self' https://apis.google.com; report-uri http://example.org/my_amazing_csp_report_parser"
}
}
Il report contiene informazioni utili per trovare la causa di una violazione delle norme, tra cui la pagina in cui si è verificata (document-uri
), il referrer
della pagina, la risorsa che ha violato le norme della pagina (blocked-uri
), la direttiva specifica violata (violated-directive
) e le norme complete della pagina (original-policy
).
Solo report
Se stai appena iniziando a utilizzare CSP, ti consigliamo di utilizzare la modalità solo report per valutare lo stato della tua app prima di modificare le norme. Per farlo,
invece di inviare un'intestazione Content-Security-Policy
, invia un'intestazione
Content-Security-Policy-Report-Only
:
Content-Security-Policy-Report-Only: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;
I criteri specificati in modalità solo report non bloccano le risorse con limitazioni, ma inviano i report sulle violazioni alla posizione specificata. Puoi anche inviare entrambe le intestazioni per applicare un criterio e monitorarne un altro. Si tratta di un ottimo modo per testare le modifiche al CSP e applicare al contempo le norme attuali: attiva i report per una nuova norma, monitora i report sulle violazioni e correggi eventuali bug. Quando ritieni che la nuova norma sia soddisfacente, inizia ad applicarla.
Utilizzo reale
Il primo passo per creare un criterio per la tua app è valutare le risorse caricate. Una volta compresa la struttura dell'app, crea un criterio in base ai relativi requisiti. Le sezioni seguenti illustrano alcuni casi d'uso comuni e la procedura decisionale per supportarli in base alle linee guida del fornitore di servizi cloud.
Widget di social media
- Il pulsante Mi piace di Facebook
offre diverse opzioni di implementazione. Ti consigliamo di utilizzare la versione
<iframe>
per mantenere la sandbox separata dal resto del sito. Per funzionare correttamente, è necessaria un'istruzionechild-src https://facebook.com
. - Il pulsante Tweet di X si basa sull'accesso a uno script.
Sposta lo script fornito in un file JavaScript esterno e utilizza l'istruzione
script-src https://platform.twitter.com; child-src https://platform.twitter.com
. - Altre piattaforme hanno requisiti simili e possono essere affrontati in modo simile.
Per testare queste risorse, ti consigliamo di impostare un valore
default-src
pari a'none'
e di guardare la console per determinare quali risorse dovrai abilitare.
Per utilizzare più widget, combina le istruzioni come segue:
script-src https://apis.google.com https://platform.twitter.com; child-src https://plusone.google.com https://facebook.com https://platform.twitter.com
Lockdown
Per alcuni siti web, è consigliabile assicurarsi che sia possibile caricare solo risorse locali. L'esempio seguente sviluppa un CSP per un sito di banking, a partire da un criterio predefinito che blocca tutto (default-src 'none'
).
Il sito carica tutte le immagini, gli stili e gli script da una CDN all'indirizzo
https://cdn.mybank.net
e si connette a https://api.mybank.com/
utilizzando XHR per recuperare i dati. Utilizza frame, ma solo per le pagine locali del sito (nessuna origine di terze parti). Non c'è Flash sul sito, né caratteri, né funzioni extra. L'intestazione CSP più restrittiva che può inviare è la seguente:
Content-Security-Policy: default-src 'none'; script-src https://cdn.mybank.net; style-src https://cdn.mybank.net; img-src https://cdn.mybank.net; connect-src https://api.mybank.com; child-src 'self'
Solo SSL
Di seguito è riportato un esempio di CSP per un amministratore di forum che vuole assicurarsi che tutte le risorse del forum vengano caricate solo tramite canali sicuri, ma non ha esperienza di programmazione e non dispone delle risorse per riscrivere il software del forum di terze parti pieno di script e stili in linea:
Content-Security-Policy: default-src https:; script-src https: 'unsafe-inline'; style-src https: 'unsafe-inline'
Anche se https:
è specificato in default-src
, le direttive script e style non ereditano automaticamente questa origine. Ogni istruzione sovrascrive l'impostazione predefinita
per quel tipo specifico di risorsa.
Sviluppo dello standard CSP
Il criterio di sicurezza del contenuto di livello 2 è uno standard consigliato di W3C. Il Web Application Security Working Group di W3C sta sviluppando la prossima iterazione della specifica, Content Security Policy Level 3.
Per seguire la discussione su queste funzionalità imminenti, consulta gli archivi della mailing list public-webappsec@.