L’autore ha selezionato Girls Who Code per ricevere una donazione come parte del programma Scrivi per Donazioni.
Introduzione
Quando visiti un sito web, vengono utilizzate varie risorse per caricarlo e renderlo. Ad esempio, quando vai su https://www.digitalocean.com
, il tuo browser scarica l’HTML e il CSS direttamente da digitalocean.com
. Tuttavia, le immagini e altri asset vengono scaricati da assets.digitalocean.com
, e gli script di analisi vengono caricati dai rispettivi domini.
Alcuni siti web utilizzano una moltitudine di servizi, stili e script diversi per caricare e renderizzare i loro contenuti, e il tuo browser eseguirà tutto ciò. Un browser non sa se il codice è dannoso, quindi è responsabilità dello sviluppatore proteggere gli utenti. Poiché può esserci molte risorse su un sito web, avere una funzionalità nel browser che consente solo risorse approvate è un buon modo per garantire che gli utenti non vengano compromessi. A questo scopo servono le Politiche di Sicurezza dei Contenuti (CSP).
Utilizzando un’intestazione CSP, uno sviluppatore può consentire esplicitamente l’esecuzione di determinate risorse impedendo l’esecuzione di tutte le altre. Poiché la maggior parte dei siti può avere fino a 100 risorse e ognuna deve essere approvata per la categoria specifica di risorsa che è, implementare una CSP può essere un compito noioso. Tuttavia, un sito web con una CSP sarà più sicuro poiché garantisce che solo le risorse approvate siano autorizzate a eseguire.
In questo tutorial, implementerai un CSP in un’applicazione Django di base. Personalizzerai il CSP per consentire l’esecuzione di determinati domini e risorse inline. Opzionalmente, puoi anche utilizzare Sentry per registrare le violazioni.
Prerequisiti
Per completare questo tutorial, avrai bisogno di:
- A working Django project (version 3 or greater is preferred), either on your local machine or a DigitalOcean Droplet. If you don’t have one, you can create one with the tutorial, How to Install Django and Set Up a Development Environment on Ubuntu 20.04.
- A web browser like Firefox or Chrome and an understanding of browser network tools. For more on using browser network tools, check out the product documentation for the Network Monitor in Firefox or the DevTools Network Tab in Chrome. For more general guidance on browser developer tools, see the guide: What are Browser Developer Tools?
- Conoscenza di Python 3 e Django, che puoi acquisire dalla serie di tutorial, Come Programmare in Python e Sviluppo con Django.
- Un account su Sentry per tracciare le violazioni del CSP (opzionale).
Passaggio 1 — Creazione di una Vista Demo
In questo passaggio, modificherai il modo in cui la tua applicazione gestisce le viste in modo da poter aggiungere il supporto CSP.
Come prerequisito, hai installato Django e configurato un progetto di esempio. La vista predefinita in Django è troppo semplice per dimostrare tutte le capacità del middleware CSP, quindi creerai una semplice pagina HTML per questo tutorial.
Naviga nella cartella del progetto che hai creato nei prerequisiti:
Mentre sei all’interno della directory django-apps
, crea il tuo ambiente virtuale. Lo chiameremo il generico env
, ma dovresti usare un nome significativo per te e il tuo progetto.
Ora, attiva l’ambiente virtuale con il seguente comando:
All’interno dell’ambiente virtuale, crea un file views.py
nella cartella del tuo progetto utilizzando nano
, o il tuo editor di testo preferito:
Ora, aggiungerai una vista di base che renderà un template index.html
che creerai dopo. Aggiungi quanto segue a views.py
:
Salva e chiudi il file quando hai finito.
Crea un template index.html
in una nuova directory templates
:
Aggiungi quanto segue a index.html
:
La vista che abbiamo creato renderà questa semplice pagina HTML. Visualizzerà il testo Ciao, Sammy! insieme a un’immagine di Sammy lo squalo.
Salva e chiudi il file quando hai finito.
Per accedere a questa vista, dovrai aggiornare urls.py
:
Importa il file views.py
e aggiungi una nuova route aggiungendo le linee evidenziate:
La nuova vista appena creata sarà ora visibile quando visiti /
(quando l’applicazione è in esecuzione).
Salva e chiudi il file.
Infine, dovrai aggiornare INSTALLED_APPS
per includere testsite
in settings.py
:
Qui, aggiungi testsite
all’elenco delle applicazioni in settings.py
in modo che Django possa fare alcune ipotesi sulla struttura del tuo progetto. In questo caso, si presumirà che la cartella templates
contenga modelli Django che puoi utilizzare per la visualizzazione delle viste.
Dalla directory principale del progetto (testsite
), avvia il server di sviluppo di Django con il seguente comando, sostituendo your-server-ip
con l’indirizzo IP del tuo server.
Apri un browser e visita your-server-ip:8000
. La pagina dovrebbe assomigliare a questa:
In questo momento, la pagina mostra un’immagine del profilo di Sammy lo squalo. Sotto l’immagine c’è il testo Ciao, Sammy! in scrittura blu.
Per interrompere il server di sviluppo di Django, premi CONTROL-C
.
In questo passaggio, hai creato una vista di base che funge da homepage del tuo progetto Django. Successivamente, aggiungerai il supporto CSP alla tua applicazione.
Passaggio 2 — Installazione del middleware CSP
In questo passaggio, installerai e implementerai un middleware CSP in modo da poter aggiungere intestazioni CSP e lavorare con le funzionalità CSP nelle tue viste. I middleware aggiungono funzionalità aggiuntive a qualsiasi richiesta o risposta gestita da Django. In questo caso, il Middleware Django-CSP aggiunge il supporto CSP alle risposte di Django.
Prima, installerai il Middleware CSP di Mozilla nel tuo progetto Django utilizzando pip
, il gestore di pacchetti di Python. Utilizza il seguente comando per installare il pacchetto necessario da PyPi, l’Indice dei Pacchetti Python. Per eseguire il comando, puoi interrompere il server di sviluppo di Django utilizzando CONTROL-C
oppure aprire una nuova scheda nel tuo terminale:
Successivamente, aggiungi il middleware alle impostazioni del tuo progetto Django. Apri settings.py
:
Con django-csp
installato, ora puoi aggiungere il middleware in settings.py
. Questo aggiungerà gli header CSP alle tue risposte.
Aggiungi la seguente riga all’array di configurazione MIDDLEWARE
:
Salva e chiudi il file quando hai finito. Il tuo progetto Django ora supporta i CSP. Nel prossimo passaggio, inizierai ad aggiungere gli header CSP.
Passaggio 3 — Implementazione di un Header CSP
Ora che il tuo progetto supporta i CSP, è pronto per essere rafforzato dal punto di vista della sicurezza. Per ottenere ciò, configurerai il progetto per aggiungere gli header CSP alle tue risposte. Un header CSP è ciò che dice al browser come comportarsi quando incontra un determinato tipo di contenuto. Quindi, se l’header dice di consentire solo le immagini da un determinato dominio, allora il browser consentirà solo le immagini da quel dominio.
Utilizzando nano o il tuo editor di testo preferito, apri settings.py
:
Definisci le seguenti variabili in qualsiasi punto del file:
Queste regole sono il modello di base per la tua CSP. Queste linee indicano quali origini sono consentite per le immagini, i fogli di stile e gli script, rispettivamente. Al momento, contengono tutte la stringa 'self'
, il che significa che sono consentite solo risorse dal proprio dominio.
Salva e chiudi il file quando hai finito.
Esegui il tuo progetto Django con il seguente comando:
Quando visiti l'indirizzo-ip-del-tuo-server:8000
, vedrai che il sito è rotto:
Come previsto, l’immagine non appare e il testo appare con lo stile predefinito (grassetto nero). Ciò significa che l’intestazione CSP viene applicata e la nostra pagina è ora più sicura. Poiché la vista che hai creato in precedenza fa riferimento a fogli di stile e immagini da domini che non sono tuoi, il browser li blocca.
Il tuo progetto ha ora una CSP funzionante che indica al browser di bloccare le risorse che non provengono dal tuo dominio. Successivamente, modificherai la CSP per consentire risorse specifiche, il che risolverà il problema dell’immagine mancante e dello stile sulla homepage.
Passaggio 4 — Modifica della CSP per consentire risorse esterne
Ora che hai un CSP di base, lo modificherai in base a ciò che stai utilizzando sul tuo sito. Ad esempio, un sito web che utilizza Adobe Fonts e video incorporati da YouTube avrà bisogno di consentire tali risorse. Tuttavia, se il tuo sito web mostra solo immagini dal proprio dominio, puoi lasciare le impostazioni delle immagini ai loro valori predefiniti restrittivi.
Il primo passo è individuare ogni risorsa che devi approvare. Puoi utilizzare gli strumenti per sviluppatori del tuo browser per farlo. Apri il Monitor di rete in Ispeziona elemento, aggiorna la pagina e guarda le risorse bloccate:
Il registro di rete mostra che due risorse sono bloccate dal CSP: un foglio di stile da fonts.googleapis.com e un’immagine da html.sammy-codes.com. Per consentire queste risorse nell’intestazione del CSP, dovrai modificare le variabili in settings.py.
Per consentire risorse da domini esterni, aggiungi il dominio alla parte del CSP che corrisponde al tipo di file. Quindi, per consentire un’immagine da html.sammy-codes.com, aggiungerai html.sammy-codes.com a CSP_STYLE_SRC.
Apri settings.py e aggiungi quanto segue alla variabile CSP_STYLE_SRC:
Ora, invece di consentire solo immagini dal tuo dominio, il sito consente anche immagini da html.sammy-codes.com.
La vista dell’indice utilizza i Google Fonts. Google fornisce al tuo sito i font (da https://fonts.gstatic.com
) e uno stile per applicarli (da https://fonts.googleapis.com
). Per consentire il caricamento dei font, aggiungi quanto segue alla tua CSP:
Similmente al consentire le immagini da html.sammy-codes.com
, consentirai anche i fogli di stile da fonts.googleapis.com
e i font da fonts.gstatic.com
. Per contesto, lo stile caricato da fonts.googleapis.com
è utilizzato per applicare i font. I font stessi vengono caricati da fonts.gstatic.com
.
Salva e chiudi il file.
Avvertenza: Similmente a self
, ci sono altre parole chiave come unsafe-inline
, unsafe-eval
, o unsafe-hashes
che possono essere utilizzate in una CSP. È altamente consigliato evitare l’uso di queste regole nella tua CSP. Sebbene rendano più semplice l’implementazione, possono essere utilizzate per aggirare la CSP e renderla inutile.
Per maggiori informazioni, consulta la documentazione del prodotto Mozilla per “Script inline non sicuro”.
Ora, i Google Fonts saranno autorizzati a caricare stili e font sul tuo sito e html.sammy-codes.com
sarà autorizzato a caricare immagini. Tuttavia, quando visiti una pagina sul tuo server, potresti notare che vengono caricare solo le immagini. Questo perché gli stili inline nell’HTML utilizzati per applicare i font non sono consentiti. Risolverai questo nel prossimo passaggio.
Passo 5 — Lavorare con Script e Stili In-Linea
A questo punto, hai modificato il CSP per consentire risorse esterne. Tuttavia, le risorse in linea, come stili e script nella tua visualizzazione, non sono ancora consentiti. In questo passaggio, li farai funzionare in modo che tu possa applicare lo stile dei caratteri.
Ci sono due modi per consentire script e stili in linea: nonce e hash. Se scopri che modifichi frequentemente gli script e gli stili in linea, utilizza i nonce per evitare modifiche frequenti al tuo CSP. Se raramente aggiorni gli script e gli stili in linea, utilizzare gli hash è un’opzione ragionevole.
Utilizzo di nonce
per Consentire Script In-Linea
Innanzitutto, utilizzerai l’approccio nonce. Un nonce è un token generato casualmente che è unico per ogni richiesta. Se due persone visitano il tuo sito, ognuna riceverà un nonce
unico che è incorporato negli script e negli stili in linea che approvi. Pensa al nonce come a una password monouso che approva determinate parti di un sito per una singola sessione.
Per aggiungere il supporto per il nonce al tuo progetto, aggiornerai il tuo CSP in settings.py
. Apri il file per la modifica:
Aggiungi script-src
in CSP_INCLUDE_NONCE_IN
nel file settings.py
.
Definisci CSP_INCLUDE_NONCE_IN
ovunque nel file e aggiungi 'script-src'
ad esso:
CSP_INCLUDE_NONCE_IN
indica a quali script inline è consentito aggiungere attributi nonce
. CSP_INCLUDE_NONCE_IN
è gestito come un array poiché più origini dati supportano i nonces (ad esempio, style-src
).
Salva e chiudi il file.
Adesso è consentito generare nonces per gli script inline quando si aggiunge l’attributo nonce
ad essi nel tuo modello di visualizzazione. Per provare ciò, userai un semplice frammento JavaScript.
Apri index.html
per modificarlo:
Aggiungi il seguente frammento nell’<head>
dell’HTML:
Questo frammento stampa Ciao dalla console!"
sulla console del browser. Tuttavia, poiché il tuo progetto ha una CSP che consente solo script inline se hanno un nonce
, questo script non verrà eseguito e produrrà invece un errore.
Puoi vedere questo errore nella console del browser quando aggiorni la pagina:
L’immagine si carica perché hai consentito risorse esterne nel passaggio precedente. Come previsto, lo stile è attualmente predefinito perché non hai ancora consentito stili inline. Anche come previsto, il messaggio di console non è stato stampato e ha restituito un errore. Dovrai fornirgli un nonce
per approvarlo.
Puoi farlo aggiungendo nonce="{{request.csp_nonce}}"
a questo script come attributo. Apri index.html
per modificarlo e aggiungi la porzione evidenziata come mostrato qui:
Salva e chiudi il file quando hai finito.
Se ricarichi la pagina, lo script verrà ora eseguito:
Quando guardi in Ispeziona Elemento, noterai che non c’è alcun valore per l’attributo:
Il valore non appare per motivi di sicurezza. Il browser ha già elaborato il valore. È nascosto in modo che gli script con accesso al DOM non possano accedervi e applicarlo ad un altro script. Se invece fai Visualizza sorgente pagina, questo è ciò che il browser ha ricevuto:
Nota che ogni volta che aggiorni la pagina, il valore di nonce
cambia. Questo perché il middleware CSP nel nostro progetto genera un nuovo nonce
per ogni richiesta.
Questi valori di nonce
vengono aggiunti all’intestazione CSP quando il browser riceve la risposta:
Ogni richiesta che il browser fa al tuo sito avrà un valore di nonce
univoco per quello script. Poiché il nonce
è fornito nell’intestazione CSP, significa che il server Django ha approvato quell’istruzione specifica per essere eseguita.
Hai aggiornato il tuo progetto per funzionare con nonce, che possono essere applicati a risorse multiple. Ad esempio, puoi applicarlo anche agli stili, aggiornando CSP_INCLUDE_NONCE_IN
per consentire style-src
. Ma c’è un approccio più semplice per approvare le risorse inline, ed è quello che farai dopo.
Utilizzo degli Hash per Consentire Stili Inline
Un altro approccio per consentire script e stili inline è tramite gli hash. Un hash è un identificatore univoco per una risorsa inline specifica.
Come esempio, questo è lo stile inline nel nostro modello:
Attualmente, però, gli stili non funzionano. Quando si visualizza il sito nel browser, le immagini vengono caricate correttamente, ma i font e gli stili non vengono applicati:
Nella console del browser, troverai un errore che indica che uno stile inline viola il CSP. (Potrebbero esserci altri errori, ma cerca l’errore relativo allo stile inline.)
L’errore è prodotto perché lo stile non è approvato dal nostro CSP. Ma, nota che l’errore fornisce l’hash necessario per approvare il frammento di stile. Questo hash è univoco per questo specifico frammento di stile. Nessun altro frammento avrà mai lo stesso hash. Quando questo hash viene inserito all’interno del CSP, ogni volta che questo specifico stile viene caricato, verrà approvato. Ma, se mai modifichi questi stili, dovrai ottenere il nuovo hash e sostituire quello vecchio con esso nel CSP.
Ora applicherai l’hash aggiungendolo a CSP_STYLE_SRC
in settings.py
, in questo modo:
Aggiungere l’hash sha256-...
alla lista CSP_STYLE_SRC
consentirà al browser di caricare il foglio di stile senza errori.
Salva e chiudi il file.
Ora ricarica il sito nel browser e i caratteri e gli stili dovrebbero caricarsi correttamente:
Gli stili e gli script inline ora funzionano correttamente. In questo passaggio hai utilizzato due approcci diversi, nonce e hash, per consentire stili e script inline.
Tuttavia, c’è un problema importante da affrontare. Le CSP sono tediose da mantenere, specialmente per siti web grandi. Potresti aver bisogno di un modo per tracciare quando la CSP blocca una risorsa in modo da poter determinare se si tratta di una risorsa dannosa o semplicemente di una parte non funzionante del tuo sito. Nel prossimo passaggio, userai Sentry per registrare e tenere traccia di tutte le violazioni prodotte dalla tua CSP.
Passaggio 6 — Segnalazione delle violazioni con Sentry (Opzionale)
Dato quanto tendono ad essere restrittive le CSP, è utile sapere quando bloccano contenuti, specialmente poiché il blocco dei contenuti probabilmente significa che alcune funzionalità sul tuo sito non funzioneranno. Strumenti come Sentry possono informarti quando la CSP blocca le richieste degli utenti. In questo passaggio, configurerai Sentry per registrare e segnalare le violazioni della CSP.
Come prerequisito, ti sei registrato per un account con Sentry. Ora creerai un progetto.
Nell’angolo in alto a sinistra della dashboard di Sentry, fai clic sulla scheda Progetti:
Nell’angolo in alto a destra, fai clic sul pulsante Crea Progetto:
Vedrai una serie di loghi con un titolo che indica Scegli una piattaforma. Scegli Django:
Quindi, in fondo, nomina il tuo progetto (per questo esempio, useremo sammys-tutorial
), e clicca sul pulsante Crea Progetto:
Sentry ti fornirà un frammento di codice da aggiungere al tuo file settings.py
. Salva questo frammento da aggiungere in un passaggio successivo.
Nel tuo terminale, installa il SDK di Sentry:
Apri settings.py
in questo modo:
Aggiungi quanto segue alla fine del file, e assicurati di sostituire SENTRY_DSN
con il valore dal cruscotto:
Questo codice è fornito da Sentry in modo che possa registrare eventuali errori che si verificano nella tua applicazione. È la configurazione predefinita per Sentry e inizializza Sentry per la registrazione di problemi sul nostro server. Tecnicamente, non è necessario inizializzare Sentry sul tuo server per le violazioni di CSP, ma nel raro caso ci siano problemi nel rendering di nonces o hash, questi errori verranno registrati su Sentry.
Salva e chiudi il file.
Successivamente, torna alla dashboard del tuo progetto e clicca sull’icona dell’ingranaggio per accedere alle Impostazioni:
Vai alla scheda Intestazioni di Sicurezza:
Copia il report-uri
:
Aggiungilo al tuo CSP come segue:
Assicurati di sostituire il-tuo-report-uri
con il valore che hai copiato dalla dashboard.
Salva e chiudi il tuo file. Ora, quando l’applicazione della CSP causa una violazione, Sentry la registrerà su questo URI. Puoi provare questo rimuovendo un dominio o un hash dalla tua CSP, o rimuovendo il nonce
dallo script che hai aggiunto in precedenza. Carica la pagina nel browser e vedrai l’errore nella pagina Problemi di Sentry:
Se scopri di essere sopraffatto dal numero di registrazioni, puoi anche definire
CSP_REPORT_PERCENTAGE
in settings.py
per inviare solo un certo percentuale dei log a Sentry.
Ora ogni volta che c’è una violazione della CSP, riceverai una notifica e potrai visualizzare l’errore in Sentry.
Conclusione
In questo articolo, hai protetto la tua applicazione Django con una politica di sicurezza del contenuto. Hai aggiornato la tua politica per consentire risorse esterne e utilizzare nonce e hash per consentire script e stili inline. Hai anche configurato l’invio delle violazioni a Sentry. Come prossimo passo, dai un’occhiata alla documentazione di Django CSP per saperne di più su come applicare la tua CSP.