CSP: Content Security Policy, come funziona

I contenuti della pagina web

Quando navighiamo in un sito internet alcuni contenuti come i fogli di stile CSS, le immagini, gli script JavaScript sono spesso caricati da dei file.

Dico spesso perché volendo potremmo inserire CSS, JS e immagini completamente dentro la nostra pagina HTML, ma ne parleremo in un altro articolo.

Questi contenuti possono essere presenti nello stesso server oppure potrebbero essere risorse caricate da sorgenti esterne, da altri siti o da CDN.

Putroppo non solo i contenuti che noi riteniamo necessari e corretti vengono caricati in questo modo. A volte virus e malware, dopo aver infettato un sito internet, cercano di caricare delle payload da fonti esterne. Lo fanno per vari motivi:

  • utilizzare un malware più semplice e leggero da inserire nei codici del sito;
  • permettere al malware di scaricare moduli di volta in volta aggiornati;
  • rendere più difficile l’identificazione.

Come possiamo evitare contenuti non desiderati?

Ci sono molti modi, più o meno standard, per limitare i rischi. Sicuramente utilizzare dei WAF ovvero dei Web Application Firewall aiuta, riducendo la possibilità che comportamenti anomali o pericolosi compromettano l’integrità del nostro sito internet. Questo può ridurre la superficie di attacco sia lato front-end, sia lato back-end. Personalmente mi trovo molto bene con strumenti come:

Software e servizi di questo tipo sono molto comodi, facilmente integrabili e testati.

Ma spesso non basta. Alcune infezioni possono comunque riuscire. Non esiste mai un metodo che garantisca al 100% la sicurezza del vostro sito. Se qualcuno vi promette quello tendenzialmente mente. Possiamo però rendere la vita più complicata agli attaccanti.

CSP, cosa sono?

Le Content Security Policy, dette più brevemente CSP, sono delle regole che informano il browser su come comportarsi quando deve caricare i contenuti della nostra pagina HTML. Aggiungono quindi un layer di sicurezza al nostro sito specialmente nei confronti degli attacchi Cross-Site Scripting, detti XSS.

Abbiamo due modi per inviare queste policy al browser:

  • tramite header HTTP Content-Security-Policy;
  • tramite meta interni alla pagina stessa.

Una regola CSP informa il browser che i contenuti possono essere caricati solo da certe fonti che lo sviluppatore o l’amministratore più in generale ritiene valide e sicure. Oltre alle sorgenti esterne possiamo specificare anche regole per i contenuti inline e quindi i contenuti quali CSS, JavaScript ecc presenti direttamente nella nostra pagina.

In caso estremi potete addirittura disabilitare forzatamente l’esecuzione di script JavaScript, da qualunque sorgente.

Come sono fatte le regole CSP?

Le regole CSP sono composte da una serie di direttive, ognuna delle quali descrive la policy per un determinato tipo di contenuto. Vediamole assieme.

La regola base default-src

Questa è la policy di default, ci deve essere sempre. Indica come si deve comportare il browser nel caso di contenuti senza una policy specifica.

direttivacontenuto
script-srcServe per specificare la policy per gli script. Se presente sovrascrive per gli script la policy specificata con default-src
style-srcSpecifica la policy per i fogli di stile del sito internet. Come per script-src sovrascrive la policy di default
font-srcSpecifica la policy per i le font caricate tramite @font-face, sovrascrive la policy di default
media-srcSpecifica la policy per i contenuti caricati tramite <audio>, <video> e <track>, sovrascrive la policy di default
frame-srcSpecifica la policy per i contenuti caricati tramite <frame> e <iframe>, sovrascrive la policy di default
img-srcSpecifica la policy per i immagini e favicon, sovrascrive la policy di default

Per la lista completa vi rimando al sito MDN - Content-Security-Policy

Altre direttive spesso non utilizzate ma molto interessanti sono:

direttivacontenuto
form-actionLimita gli URL che possono essere utilizzati come target per i form
frame-ancestorsSpecifica i parent validi per l’embed della nostra pagina

CSP come header, alcuni esempi

Questa regola caricherà i contenuti solo dallo stesso dominio, escludendo anche i sotto domini.

Content-Security-Policy: default-src 'self'

Se invece vogliamo caricare i contenuti anche da un altro dominio e anche dai suoi sotto domini possiamo scivere qualcosa di questo tipo.

Content-Security-Policy: default-src 'self' example.com *.example.com

Possiamo combinare più direttive:

  • una di default che accetta contenuti solo dal dominio principale;
  • una img-src che permette di caricare immagini da ogni origine;
  • una media-src che permette di caricare contenuti solo dai due domini indicati;
  • una script-src che permette di caricare gli script solo dal singolo dominio di terzo livello specificato.
Content-Security-Policy: default-src 'self'; img-src *; media-src example.org example.net; script-src userscripts.example.com

CSP come meta

Nel caso volessimo invece inviare le policy tramite tag meta possiamo impostarle come segue

<meta
  http-equiv="Content-Security-Policy"
  content="default-src 'self'; img-src https://*; child-src 'none';" />

Attenzione se usate la forma meta non è possibile inviare le violazioni CSP come vedremo tra poco. Questo è possibile solo tramite invio nella forma header.

Come testare se la policy funziona correttamente?

Quando impostiamo le regole CSP conviene sempre testare se tutto funziona correttamente. A volte può capitare che un contenuto o una dinamica del nostro sito non funzioni più come prima. Un classico è quando impostiamo delle direttive per degli script di web analytics che poi a loro volta tentano di caricare altri script di utilità da altri domini.

Possiamo procedere a mano, controllando in console quali script non vengono più caricati e aggiungendo gli eventuali domini e correggendo le regole.

Se il sito è live però potrebbe non essere gradevole ritrovarselo non funzionante.

Possiamo invece, solo nella forma header, inviare l’intestazione Content-Security-Policy-Report-Only che specifica che le regole indicate sono solo in modalità test, non vengono bloccati i contenuti e sono invece riportate all’indirizzo specificato le eventuali problematiche.

Content-Security-Policy-Report-Only: default-src https:; report-to /endpoint-dove-inviare-le-segnalazioni/

Possiamo altresì indicare che le policy siano applicate e quindi i contenuti che non le rispettano bloccate, inviando al tempo stesso le problematiche al nostro end-point. Per fare questo usiamo il precedente header, ovvero Content-Security-Policy.

Content-Security-Policy: default-src https:; report-to /endpoint-dove-inviare-le-segnalazioni/

Il report inviato è un oggetto JSON con un Content-Type application/csp-report con tutti i dati che trovate specificati nella documentazione MDN “Violation report syntax”.

Esempio

Preso direttamente dalla documentazione ufficiale, trovate un esempio con un HTML, una regola inviata tramite header e il report che arriverà all’end-point indicato nella direttiva. Ipotizziamo che questa pagina sia presente all’indirizzo http://example.com/signup.html.

<!doctype html>
<html lang="en-US">
  <head>
    <meta charset="UTF-8" />
    <title>Sign Up</title>
    <link rel="stylesheet" href="css/style.css" />
  </head>
  <body>
    Page content
  </body>
</html>
Content-Security-Policy-Report-Only: default-src 'none'; style-src cdn.example.com; report-to /_/csp-reports

In questo caso il foglio di stile può essere caricato solo dal dominio cdn.example.com come specificato nella regola. La nostra pagina HTML contiene invece un tag link che tenta il caricamento da un percorso relativo e quindi dalla stessa origine del sito. Questo rappresenta una violazione e per il fatto che abbiamo speficato che report-to deve inviare le problematiche riceveremo un oggetto JSON come segue:

{
  "csp-report": {
    "blocked-uri": "http://example.com/css/style.css",
    "disposition": "report",
    "document-uri": "http://example.com/signup.html",
    "effective-directive": "style-src-elem",
    "original-policy": "default-src 'none'; style-src cdn.example.com; report-to /_/csp-reports",
    "referrer": "",
    "status-code": 200,
    "violated-directive": "style-src-elem"
  }
}

Conclusioni

Come abbiamo visto possiamo migliorare la sicurezza del nostro sito aggiungendo un livello di protezione in più, complicando la vita a bot e malintenzionati.

Le notizie dei più disparati attacchi, anche a grosse entità, sono quotidiane. Date un occhio ad esempio in questi ottimi siti web:

Come già detto nessun sistema da solo o in gruppo permette di avere una certezza assoluta che un sito non possa essere attaccato. La superficie disponibile rimane sempre ampia. Il CMS, il server software, il server hardware, le persone possono rappresentare un punto di ingresso per i malintenzionati.

A prescindere dai sistemi di protezione, un consiglio rimane sempre valido: fate backup, spesso e verificateli e stoccateli da un’altra parte possibilmente offline.

Ma ne parleremo, buon coding!