Norme di sicurezza dei contenuti

I criteri di sicurezza del contenuto possono ridurre significativamente il rischio e l'impatto di attacchi cross-site scripting nei browser moderni.

Joe Medley
Joe Medley

Supporto dei browser

  • 25
  • 14
  • 23
  • 7

Fonte

Il modello di sicurezza del web si basa su un criterio della stessa origine. Ad esempio, il codice di https://mybank.com deve avere accesso solo ai dati di https://mybank.com e a https://evil.example.com non deve mai essere consentito l'accesso. Ogni origine viene, in teoria, tenuta isolata dal resto del web, offrendo agli sviluppatori una sandbox sicura in cui integrare. In pratica, però, gli aggressori hanno trovato diversi modi per sovvertire il sistema.

Gli attacchi cross-site scripting (XSS), ad esempio, eludono il criterio della stessa origine con l'inganno un sito a fornire codice dannoso insieme ai contenuti previsti. Questo è un enorme problema, poiché i browser considerano attendibile tutto il codice visualizzato in una pagina come parte legittima dell'origine di sicurezza di quella pagina. La scheda di riferimento XSS è una vecchia ma rappresentativa dei metodi che un utente malintenzionato potrebbe utilizzare per violare questa fiducia iniettando codice dannoso. Se un utente malintenzionato inserisce correttamente qualsiasi codice, ha compromesso la sessione utente e ottenuto l'accesso a informazioni private.

Questa pagina descrive il criterio di sicurezza del contenuto (CSP) come strategia per ridurre il rischio e l'impatto di attacchi XSS nei browser moderni.

Componenti di CSP

Per implementare un CSP efficace, segui questi passaggi:

  • Utilizza le liste consentite per comunicare al cliente cosa è consentito e cosa non lo è.
  • Scopri quali istruzioni sono disponibili.
  • Impara a conoscere le parole chiave che utilizzano.
  • Limita l'uso del codice incorporato e di eval().
  • Segnala le violazioni delle norme al tuo server prima di applicarle.

Liste consentite di origini

Gli attacchi XSS sfruttano l'incapacità del browser di distinguere tra script che fanno parte della tua applicazione e script inseriti malevolmente da terze parti. Ad esempio, il pulsante +1 di Google in fondo a questa pagina viene caricato ed eseguito il codice da 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 da solo che il codice di apis.google.com è sicuro da eseguire, al contrario del codice di apis.evil.example.com. Il browser scarica ed esegue volentieri il codice richiesto da una pagina, indipendentemente dalla fonte.

L'intestazione HTTP Content-Security-Policy di CSP consente di creare una lista consentita di fonti di contenuti attendibili e indica al browser di eseguire o visualizzare solo le risorse di queste origini. Anche se un utente malintenzionato riesce a individuare un'apertura da cui inserire uno script, quest'ultimo non corrisponderà alla lista consentita e, di conseguenza, non verrà eseguito.

Confidiamo nel fatto che apis.google.com fornisca codice valido e confidiamo che anche noi stessi facciamo lo stesso. Ecco 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 è un'istruzione che controlla un insieme di privilegi relativi agli script per una pagina. Questa intestazione 'self' è una sorgente 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, oltre che dall'origine della pagina corrente, ma non da qualsiasi altra origine. Se un utente malintenzionato inserisce un codice nel tuo sito, il browser genera un errore e non esegue lo script inserito.

Errore di console: impossibile caricare lo script "http://evil.example.com/evil.js" perché viola la seguente istruzione relativa a Content Security Policy: script-src "self" https://apis.google.com
La console mostra un errore quando uno script tenta di essere eseguito da un'origine non inclusa nella lista consentita.

Le norme si applicano a un'ampia gamma di risorse

CSP fornisce una serie di istruzioni relative ai criteri che consentono un controllo granulare sulle risorse che una pagina può caricare, tra cui script-src dell'esempio precedente.

Il seguente elenco illustra le altre istruzioni relative alle risorse a partire dal livello 2. È stata redatta una specifica di livello 3, ma in gran parte non è implementata nei principali browser.

base-uri
Limita gli URL che possono essere visualizzati nell'elemento <base> di una pagina.
child-src
Elenca gli URL per i worker e i contenuti dei frame incorporati. Ad esempio, child-src https://youtube.com consente di incorporare video di YouTube, ma non di altre origini.
connect-src
Limita le origini a cui puoi connetterti utilizzando XHR, WebSocket 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 endpoint validi per l'invio dai tag <form>.
frame-ancestors
Specifica le origini che possono incorporare la pagina corrente. Questa istruzione si applica ai tag <frame>, <iframe>, <embed> e <applet>. Non può essere utilizzata nei tag <meta> o per le risorse HTML.
frame-src
Questa istruzione è stata ritirata al livello 2, ma è stata ripristinata nel livello 3. Se non è presente, il browser torna a child-src.
img-src
Indica da quali origini è possibile caricare le immagini.
media-src
Limita le origini autorizzate a pubblicare video e audio.
object-src
Consente di controllare Flash e 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 segnalazioni quando viene violata una norma di sicurezza dei contenuti. Questa istruzione 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 URL cambiando HTTP in HTTPS. Questa istruzione è destinata ai siti web con un numero elevato di URL obsoleti che devono essere riscritti.
worker-src
Un'istruzione CSP di livello 3 che limita gli URL che possono essere caricati come worker, shared worker o service worker. A partire da luglio 2017, questa direttiva ha implementazioni limitate.

Per impostazione predefinita, il browser carica la risorsa associata da qualsiasi origine, senza restrizioni, a meno che non imposti un criterio con un'istruzione specifica. Per sostituire il 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 i caratteri solo da https://example.com.

Le istruzioni seguenti non usano default-src come metodo di riserva. Ricorda che non impostarli equivale a consentire qualsiasi cosa:

  • base-uri
  • form-action
  • frame-ancestors
  • plugin-types
  • report-uri
  • sandbox

Sintassi CSP di base

Per utilizzare le istruzioni CSP, elencale nell'intestazione HTTP con istruzioni 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 risorse da una rete CDN (Content Delivery Network) all'indirizzo https://cdn.example.net e non utilizza contenuti o plug-in con frame:

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 visualizzare nei tutorial online sono deprecate.

I CSP vengono definiti pagina per pagina. Dovrai inviare l'intestazione HTTP con ogni risposta che vuoi proteggere. Questo ti consente di perfezionare il criterio per pagine specifiche in base alle loro esigenze specifiche. Ad esempio, se un insieme di pagine nel tuo sito ha un pulsante +1 e le altre no, puoi consentire il caricamento del codice del pulsante solo quando necessario.

L'elenco di origini per ogni istruzione è flessibile. Puoi specificare le origini in base allo schema (data:, https:) o in base alla specificità, da solo nome host (example.com, che corrisponde a qualsiasi origine su quell'host, ovvero qualsiasi schema, qualsiasi porta) a un URI completo (https://example.com:443, che corrisponde solo a HTTPS, solo a example.com e solo alla porta 443). I caratteri jolly sono accettati, ma solo come schema, porta o nella posizione più a sinistra del nome host: *://*.example.com:* corrisponde a tutti i sottodomini di example.com (ma non example.com stesso), utilizzando qualsiasi schema, su qualsiasi porta.

L'elenco di origine accetta anche quattro parole chiave:

  • 'none' non corrisponde a nulla.
  • 'self' corrisponde all'origine corrente, ma non ai suoi sottodomini.
  • 'unsafe-inline' consente JavaScript e CSS incorporati. Per scoprire di più, consulta la sezione Evitare il codice incorporato.
  • 'unsafe-eval' consente meccanismi di conversione da testo a JavaScript come eval. Per ulteriori informazioni, consulta la sezione Evitare eval().

Queste parole chiave richiedono virgolette singole. Ad esempio, script-src 'self' (con le virgolette) autorizza l'esecuzione di JavaScript dall'host corrente; script-src self (senza virgolette) consente JavaScript da un server denominato "self" (e non dall'host attuale), il che probabilmente non è ciò che intendevi.

Sandbox delle tue pagine

C'è un'altra istruzione che vale la pena parlare: sandbox. È un po' diverso dalle altre che abbiamo visto, poiché pone delle restrizioni sulle azioni che la pagina può eseguire piuttosto che sulle 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. Questo può avere una vasta gamma di effetti sulla pagina, tra cui l'impostazione di un'origine univoca e l'impossibilità di inviare moduli. va oltre l'ambito di questa pagina, ma puoi trovare dettagli completi sugli attributi di sandbox validi nella sezione"Limitazione tramite sandbox" della specifica HTML5.

Il meta tag

Il meccanismo di consegna preferito dai CSP è un'intestazione HTTP. Può essere utile, tuttavia, impostare un criterio su una pagina direttamente nel markup. Per farlo, 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 utilizzata per frame-ancestors, report-uri o sandbox.

Evita codice incorporato

Per quanto potenti siano le liste consentite basate sull'origine utilizzate nelle direttive CSP, non sono in grado di risolvere la più grande minaccia rappresentata dagli attacchi XSS: l'inserimento di script incorporati. Se un utente malintenzionato riesce a inserire un tag script che contiene direttamente un payload dannoso (come <script>sendMyDataToEvilDotCom()</script>), il browser non ha modo di distinguerlo da un tag di script in linea legittimo. CSP risolve questo problema escludendo del tutto lo script incorporato.

Questa esclusione non include solo gli script incorporati direttamente nei tag script, ma anche i gestori di eventi incorporati e gli URL javascript:. Dovrai spostare i contenuti dei tag script in un file esterno e sostituire gli URL javascript: e <a ... onclick="[JAVASCRIPT]"> con le chiamate addEventListener() appropriate. Ad esempio, potresti riscrivere quanto segue:

<script>
    function doAmazingThings() {
    alert('YOU ARE AMAZING!');
    }
</script>
<button onclick='doAmazingThings();'>Am I amazing?</button>

ad esempio:

<!-- 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 alle best practice di web design. JavaScript incorporato combina struttura e comportamento in modi che rendono il codice poco chiaro. Inoltre, è più complicato memorizzare nella cache e compilare. Lo spostamento del codice in risorse esterne migliora il rendimento delle pagine.

Ti consigliamo vivamente di spostare i tag e gli attributi 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 incorporati

Puoi attivare script e stili incorporati aggiungendo 'unsafe-inline' come origine consentita in un'istruzione script-src o style-src. CSP livello 2 ti consente inoltre di aggiungere script incorporati specifici alla tua lista consentita utilizzando un nonce crittografico (numero usato una volta) o un hash come segue.

Per utilizzare un nonce, assegna al tag script un attributo nonce. Il suo valore deve corrispondere a uno dell'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 che segue la parola chiave nonce-:

Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'

I nonce devono essere rigenerati per ogni richiesta di pagina e devono essere non comprensibili.

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>

La norma deve includere 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>. Uso di lettere maiuscole e spazi vuoti, inclusi quelli iniziali e finali.

Le soluzioni per la generazione di hash SHA sono disponibili in un numero illimitato di lingue. Se utilizzi Chrome 40 o una versione successiva, puoi aprire DevTools e ricaricare la pagina. Nella scheda Console vengono visualizzati i messaggi di errore con l'hash SHA-256 corretto per ciascuno degli script incorporati.

Evita eval()

Anche se un utente malintenzionato non può inserire direttamente lo script, potrebbe essere in grado di ingannare la tua applicazione per indurre a convertire il testo di input in codice JavaScript eseguibile ed eseguirlo per suo conto. eval(), new Function(), setTimeout([string], …) e setInterval([string], ...) sono tutti vettori che gli utenti malintenzionati possono utilizzare per eseguire codice dannoso attraverso il testo iniettato. La risposta predefinita di CSP a questo rischio è bloccare completamente tutti questi vettori.

Questa operazione ha i seguenti effetti sulla modalità di creazione delle applicazioni:

  • Devi analizzare JSON utilizzando l'elemento JSON.parse integrato, anziché utilizzare eval. Le operazioni JSON sicure sono disponibili in tutti i browser a partire da IE8.
  • Devi riscrivere tutte le chiamate setTimeout o setInterval che effettui utilizzando funzioni incorporate anziché stringhe. Ad esempio, se la tua pagina contiene:

    setTimeout("document.querySelector('a').style.display = 'none';", 10);
    

    Riscrivilo come segue:

    setTimeout(function () {
        document.querySelector('a').style.display = 'none';
    }, 10);
      ```
    
  • Evita la creazione di modelli incorporati in fase di runtime. Molte librerie di modelli usano spesso new Function() per velocizzare la generazione dei modelli in fase di runtime, il che consente la valutazione di testo dannoso. Alcuni framework supportano CSP pronti all'uso, ricorrendo a un parser robusto in assenza di eval. L'istruzione ng-csp di AngularJS ne è un buon esempio. Tuttavia, consigliamo di utilizzare un linguaggio di modello che offre la precompilazione, ad esempio Handlebars. La precompilazione dei modelli può rendere l'esperienza utente ancora più veloce rispetto all'implementazione del runtime più rapida, oltre a rendere il tuo sito più sicuro.

Se eval() o altre funzioni da testo a JavaScript sono essenziali per la tua applicazione, puoi attivarle aggiungendo 'unsafe-eval' come origine consentita in un'istruzione script-src. Sconsigliamo vivamente di farlo a causa del rischio relativo all'inserimento di codice.

Segnalare violazioni delle norme

Per segnalare al server eventuali bug che potrebbero consentire l'inserimento di contenuti dannosi, puoi dire al browser di POST segnalare violazioni in formato JSON in una posizione specificata in un'istruzione 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), la pagina referrer, la risorsa che ha violato le norme della pagina (blocked-uri), l'istruzione specifica violata (violated-directive) e le norme complete della pagina (original-policy).

Solo report

Se hai appena iniziato a utilizzare CSP, ti consigliamo di utilizzare la modalità di solo report per valutare lo stato della tua app prima di modificare il criterio. Per farlo, anziché 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;

Il criterio specificato in modalità di solo segnalazione non blocca le risorse limitate, ma invia le segnalazioni di violazione alla località specificata. Puoi anche inviare entrambe le intestazioni per applicare un criterio e monitorarne un altro. Questo è un ottimo modo per testare le modifiche apportate al CSP e al contempo applicare le norme attuali: attiva i report per una nuova norma, monitora le segnalazioni di violazioni e correggi eventuali bug. Quando il nuovo criterio ti soddisfa, inizia ad applicarlo.

Uso reale

Il primo passaggio per creare un criterio per la tua app è valutare le risorse che carica. Quando comprendi la struttura della tua app, crea un criterio in base ai suoi requisiti. Le seguenti sezioni illustrano alcuni casi d'uso comuni e il processo decisionale per supportarli seguendo le linee guida CSP.

Widget di social media

  • Il pulsante Mi piace di Facebook ha varie opzioni di implementazione. Ti consigliamo di utilizzare la versione <iframe> per evitare che venga limitata dal resto del tuo sito. Per funzionare correttamente, è necessaria un'istruzione child-src https://facebook.com.
  • Il pulsante Tweet di X è basato 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 i problemi possono essere affrontati in modo simile. Per testare queste risorse, ti consigliamo di impostare un default-src pari a 'none' e di controllare la console per determinare quali risorse dovrai abilitare.

Per usare 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, dovrai assicurarti che possano essere caricate 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, lo stile e lo script da una rete CDN all'indirizzo https://cdn.mybank.net e si connette a https://api.mybank.com/ usando l'XHR per recuperare i dati. Utilizza i frame, ma solo per le pagine locali del sito (senza origini di terze parti). Non ci sono caratteri Flash sul sito, né caratteri, né 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 nel forum vengano caricate solo utilizzando canali sicuri, ma che non abbia esperienza di programmazione e non abbia le risorse per riscrivere software di forum di terze parti pieno di script e stili incorporati:

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 allo script e allo stile non ereditano automaticamente questa origine. Ogni istruzione sovrascrive il valore predefinito per quel tipo specifico di risorsa.

Sviluppo standard CSP

Il livello 2 del criterio di sicurezza del contenuto è 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à in arrivo, consulta gli archivi delle mailing list Public-webappsec@.