Come proteggere la tua applicazione Django con una policy di sicurezza dei contenuti

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:

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:

  1. cd django-apps

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.

  1. virtualenv env

Ora, attiva l’ambiente virtuale con il seguente comando:

  1. . env/bin/activate

All’interno dell’ambiente virtuale, crea un file views.py nella cartella del tuo progetto utilizzando nano, o il tuo editor di testo preferito:

  1. nano django-apps/testsite/testsite/views.py

Ora, aggiungerai una vista di base che renderà un template index.html che creerai dopo. Aggiungi quanto segue a views.py:

django-apps/testsite/testsite/views.py
from django.shortcuts import render

def index(request):
    return render(request, "index.html")

Salva e chiudi il file quando hai finito.

Crea un template index.html in una nuova directory templates:

mkdir django-apps/testsite/testsite/templates
nano django-apps/testsite/testsite/templates/index.html

Aggiungi quanto segue a index.html:

django-apps/testsite/testsite/templates/index.html
<!DOCTYPE html>
<html>
    <head>
        <title>Hello world!</title>
        <link rel="preconnect" href="https://fonts.googleapis.com" />
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
        <link
            href="https://fonts.googleapis.com/css2?family=Yellowtail&display=swap"
            rel="stylesheet"
        />
        <style>
            h1 {
                font-family: "Yellowtail", cursive;
                margin: 0.5em 0 0 0;
                color: #0069ff;
                font-size: 4em;
                line-height: 0.6;
            }

            img {
                border-radius: 100%;
                border: 6px solid #0069ff;
            }

            .center {
                text-align: center;
                position: absolute;
                top: 50vh;
                left: 50vw;
                transform: translate(-50%, -50%);
            }
        </style>
    </head>
    <body>
        <div class="center">
            <img src="https://html.sammy-codes.com/images/small-profile.jpeg" />
            <h1>Hello, Sammy!</h1>
        </div>
    </body>
</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:

  1. nano django-apps/testsite/testsite/urls.py

Importa il file views.py e aggiungi una nuova route aggiungendo le linee evidenziate:

django-apps/testsite/testsite/urls.py
from django.contrib import admin
from django.urls import path
from . import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.index),
]

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:

  1. nano django-apps/testsite/testsite/settings.py
django-apps/testsite/testsite/settings.py
# ...
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'testsite',
]
# ...

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.

  1. cd ~/django-apps/testsite
  2. python manage.py runserver your-server-ip:8000

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:

  1. pip install django-csp

Successivamente, aggiungi il middleware alle impostazioni del tuo progetto Django. Apri settings.py:

  1. nano testsite/testsite/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:

testsite/testsite/settings.py
MIDDLEWARE = [
    'csp.middleware.CSPMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

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:

  1. nano testsite/testsite/settings.py

Definisci le seguenti variabili in qualsiasi punto del file:

testsite/testsite/settings.py
# Politica di sicurezza del contenuto

CSP_IMG_SRC = ("'self'")

CSP_STYLE_SRC = ("'self'")

CSP_SCRIPT_SRC = ("'self'")

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:

  1. python manage.py runserver your-server-ip:8000

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:

testsite/testsite/settings.py
CSP_IMG_SRC = ("'self'", 'https://html.sammy-codes.com')

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:

testsite/testsite/settings.py
CSP_STYLE_SRC = ("'self'", 'https://fonts.googleapis.com')

CSP_FONT_SRC = ("'self'", 'https://fonts.gstatic.com/')

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:

  1. nano testsite/testsite/settings.py

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:

testsite/testsite/settings.py
# Content Security Policy

CSP_INCLUDE_NONCE_IN = ['script-src']

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:

  1. nano testsite/testsite/templates/index.html

Aggiungi il seguente frammento nell’<head> dell’HTML:

testsite/testsite/templates/index.html
<script>
    console.log("Hello from the console!");
</script>

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:

testsite/testsite/templates/index.html
<script nonce="{{request.csp_nonce}}">
    console.log("Hello from the console!");
</script>

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:

testsite/testsite/templates/index.html
<style>
    h1 {
        font-family: "Yellowtail", cursive;
        margin: 0.5em 0 0 0;
        color: #0069ff;
        font-size: 4em;
        line-height: 0.6;
    }

    img {
        border-radius: 100%;
        border: 6px solid #0069ff;
    }

    .center {
        text-align: center;
        position: absolute;
        top: 50vh;
        left: 50vw;
        transform: translate(-50%, -50%);
    }
</style>

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:

  1. nano testsite/testsite/settings.py
testsite/testsite/settings.py
CSP_STYLE_SRC = ("'self' 'sha256-r5bInLZB0y6ZxHFpmz7cjyYrndjwCeDLDu/1KeMikHA='", 'https://fonts.googleapis.com')

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:

  1. pip install --upgrade sentry-sdk

Apri settings.py in questo modo:

  1. nano testsite/testsite/settings.py

Aggiungi quanto segue alla fine del file, e assicurati di sostituire SENTRY_DSN con il valore dal cruscotto:

testsite/testsite/settings.py
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration

sentry_sdk.init(
    dsn="SENTRY_DSN",
    integrations=[DjangoIntegration()],

    # Imposta traces_sample_rate su 1.0 per catturare il 100%
    # delle transazioni per il monitoraggio delle prestazioni.
    # Consigliamo di regolare questo valore in produzione.
    traces_sample_rate=1.0,

    # Se desideri associare gli utenti agli errori (assumendo che tu stia utilizzando
    # django.contrib.auth) puoi abilitare l'invio di dati PII.
    send_default_pii=True
)

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:

testsite/testsite/settings.py
# Politica di Sicurezza del Contenuto

CSP_REPORT_URI = "your-report-uri"

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.

testsite/testsite/settings.py
# Politica di Sicurezza del Contenuto
# Invia il 10% dei log a Sentry
CSP_REPORT_PERCENTAGE = 0.1

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.

Source:
https://www.digitalocean.com/community/tutorials/how-to-secure-your-django-application-with-a-content-security-policy