Come usare la protezione CSRF di Django¶
Per trarre vantaggio dalla protezione di CSRF nelle tue view, segui questi passi:
Il middleware CSRF viene attivato di default nell’impostazione
MIDDLEWARE
. Se sovrascrivi quella impostazione, ricorda che'django.middleware.csrf.CsrfViewMiddleware'
dovrebbe stare prima di ogni middleware per le view che assume che ci si sia già occupati degli attacchi CSRF.Se l’hai disabilitato, cosa che non è raccomandata, puoi usare
csrf_protect()
sulle specifiche view che vuoi proteggere (vedi sotto).In ogni template che utilizzi un form POST, usa il tag
csrf_token
dentro l’elemento<form>
se il form fa riferimento ad un URL interna, per es.:<form method="post">{% csrf_token %}
Questo non si dovrebbe fare per i form POST che hanno come destinazione URL esterne, perchè questo esporrebbe il token CSRF, aprendo una vulnerabilità.
Nelle funzioni di view corrispondenti, assicurati che venga utilizzato
RequestContext
per fare render della response così che{% csrf_token %}
funzioni correttamente. Se stai usando la funzionerender()
, view generiche o app contrib, se già coperto perchè usano già tutteRequestContext
.
Utilizzare la protezione CSRF con AJAX¶
Anche se il metodo di cui sopra può essere utilizzato per le richieste AJAX POST, pone qualche inconveniente: devi ricordare di passare il token CSRF con i dati POST ad ogni richiesta POST. Per questo, esiste un metodo alternativo: in ogni XMLHttpRequest, imposta un header personalizzato X-CSRFToken
(come specificato nell’impostazione CSRF_HEADER_NAME
) con il valore del token CSFR. Spesso questo è più semplice, perchè molti framework Javascript offrono hooks che permettono di impostare le intestazioni per ogni richiesta.
Prima di tutto, devi ottenere il token CSRF. Come ottenerlo dipende dal fatto che siano abilitate o meno le impostazioni CSRF_USE_SESSIONS
e CSRF_COOKIE_HTTPONLY
.
Acquisire il token se CSRF_USE_SESSIONS
e CSRF_COOKIE_HTTPONLY
sono False
¶
La fonte raccomandata dalla quale prendere il token è il cookie csrftoken
, che sarà impostato se hai abilitato la protezione CSRF per le tue view come delinato precedentemente.
Il token CSFR è chiamato csrftoken
per impostazione predefinita ma puoi controllare il nome del cookie con l’impostazione CSRF_COOKIE_NAME
.
Puoi ottenere il token in questo modo:
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
const csrftoken = getCookie('csrftoken');
Il codice qui sopra può essere semplificato usando la ` libreria JavaScript Cookie <https://github.com/js-cookie/js-cookie/>`_ per rimpiazzare getCookie
:
const csrftoken = Cookies.get('csrftoken');
Nota
Il token CSRF è anche presente nel DOM in forma mascherata ma solo se esplicitamente incluso nel template usando csrf_token
. Il cookie contiene il token canonico, non mascherato. CsrfViewMiddleware
li accetterà entrambi. In ogni caso, per proteggersi da attacchi BREACH, ci si raccomanda di usare un token mascherato.
Avvertimento
Se la tua view non sta facendo rendering di un template contenente il tag di template csrf_token
, Django potrebbe non impostare il cookie CSRF. Questo accade di sovente in casi in cui i form sono aggiunti dinamicamente alla pagina. Per affrontare questo caso, Django fornisce un decoratore di view che forza l’impostazione del cookie: ensure_csrf_cookie()
.
Acquisire il token se CSRF_USE_SESSIONS
o CSRF_COOKIE_HTTPONLY
è True
¶
Se attivi CSRF_USE_SESSIONS
o CSRF_COOKIE_HTTPONLY
, devi includere il token CSRF nel tuo HTML e leggere il token dal DOM con JavaScript:
{% csrf_token %}
<script>
const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
</script>
Impostare il token sulla richiesta AJAX¶
Infine, avrai bisogno di impostare l’intestazione della tua request AJAX. Usando l’API fetch():
const request = new Request(
/* URL */,
{
method: 'POST',
headers: {'X-CSRFToken': csrftoken},
mode: 'same-origin' // Do not send CSRF token to another domain.
}
);
fetch(request).then(function(response) {
// ...
});
Usare la protezione CSRF nei template Jinja2¶
Il backend di template di Django Jinja2
aggiunge {{ csrf_input }}
al context di tutti i template che è equivalente a {% csrf_token %}
nel linguaggio di template di Django. Per esempio:
<form method="post">{{ csrf_input }}
Usare il metodo decoratore¶
Piuttosto che inserire CsrfViewMiddleware
come protezione totale, puoi usare il decoratore csrf_protect()
, che ha esattamente la stessa funzionalità su particolari view che abbiano bisogno di protezione. Devono essere usati entrambi sulle view che inseriscono il token CSRF in output e su quelle che accettano i dati POST dai form. (Sono spesso le stesse funzioni anche se non è sempre così).
L’uso del decoratore di per sè non è raccomandato poichè se dimentichi di usarlo, ci sarà un buco nella sicurezza. La strategia “a tutto tondo” di usare entrambe va bene ed introdurrà un overhead minimo.
Gestire le richieste rifiutate¶
Per impostazione predefinita, una response “403 Forbidden” viene inviata all’utente se la request in ingresso fallisce sui controlli fatti da CsrfViewMiddleware
. Questo dovrebbe accadere solo se c’è un vero Cross Site Request Forgery o quando, a causa di un errore di programmazione, il token CSRF non è stato incluso nel POST del form.
La pagina di errore, in ogni caso, non è molto amichevole quindi potresti voler fornire la tua per gestire questa condizione. Per fare ciù, imposta il setting CSRF_FAILURE_VIEW
.
I fallimenti CSRF sono registrati come warning nel logger django.security.csrf.
Usare la protezione CSRF con il caching¶
Se il tag di template csrf_token
è utilizzato da un template (o viene chiamata in qualche modo la funzione get_token
), CsrfViewMiddleware
aggiungerà un cookie ed una intestazione Vary: Cookie
alla response. Questo significa che il middleware si comporterà bene con il middleware di cache se viene usato come suggerito (UpdateCacheMiddleware
viene prima di tutti gli altri middleware).
In ogni caso, se usi decoratori di cache su view specifiche, il middleware CSRF non sarà ancora in grado di impostare l’intestazione Vary sul cookie CSRF e la response sarà messa in cache senza che ci sia. In questo caso, su qualsiasi view che richieda l’inserimento di un cookie CSRF dovresti usare prima il decoratore django.views.decorators.csrf.csrf_protect()
:
from django.views.decorators.cache import cache_page
from django.views.decorators.csrf import csrf_protect
@cache_page(60 * 15)
@csrf_protect
def my_view(request): ...
Se stai usando view basate su classi, puoi fare riferimento a Decorating class-based views.
I test e la protezione CSRF¶
CsrfViewMiddleware` sarà generalmente un grosso ostacolo nel test delle funzioni delle view, a causa della necessità del token CSRF che deve essere inviato ad ogni richiesta POST. Per questo motivo, il client HTTP di Django per i test è stato modificato per impostare un flag sulle richieste che rilassa il middleware ed il decoratore ``csrf_protect
in modo che non rifiutino più le richieste. In ogni altro aspetto (per es. invio di cookie), si comportano nello stesso modo.
Se, per qualche motivo, vuoi che il client di test esegue i controlli CSRF, puoi creare un’istanza del client di test che imponga i controlli CSRF:
>>> from django.test import Client
>>> csrf_client = Client(enforce_csrf_checks=True)
Casi limite¶
Alcune view hanno requisiti inusuali, il che significa che non ricadono nei pattern normali qui illustrati. Un certo numero di utility possono essere utili in queste situazioni. Gli scenari in cui potrebbero essere necessarie sono descritti nella seguente sezione.
Disabilitare la protezione CSRF solo per alcune view¶
Molte view richiedono la protezione CSRF ma alcune non ne hanno bisogno.
Soluzione: invece che disabilitare il middleware ed applicare csrf_protect
a tutte le view che ne hanno bisogno, abilita il middleware ed usa csrf_exempt()
.
Impostare il token quando non viene usato CsrfViewMiddleware.process_view()
¶
Ci sono casi in cui CsrfViewMiddleware.process_view
potrebbe non aver girato prima della tua view - per esempio gli handler 404 e 500 - ma hai ancora bisogno del token CSRF in un form.
Soluzione: usa requires_csrf_token()
Includere il token CSRF in una view non protetta¶
Potrebbero esserci alcune view che non sono protette e sono esenti con csrf_exempt
ma hanno ancora bisogno di includere un token CSRF.
Soluzione: usa csrf_exempt()
seguito da requires_csrf_token()
(cioè requires_csrf_token
dovrebbe essere il decoratore più interno).
Proteggere una view per un solo percorso¶
Una view ha bisogno di protezione CSRF solo in alcune condizioni e non deve averlo per tutte le altre.
Soluzione: usa csrf_exempt()
per l’intera funzione di view e csrf_protect()
per il percorso che necessita di protezione. Esempio:
from django.views.decorators.csrf import csrf_exempt, csrf_protect
@csrf_exempt
def my_view(request):
@csrf_protect
def protected_path(request):
do_something()
if some_condition():
return protected_path(request)
else:
do_something_else()
Proteggere una pagina che usa AJAX senza un form HTML¶
Una pagina fa una richiesta AJAX e non ha un form HTML con un csrf_token
che farebbe partire il cookie CSRF.
Soluzione: usa ensure_csrf_cookie()
sulla view che invia la pagina.
Protezione CSRF in applicazioni riusabili¶
Dal momento che è possibile per lo sviluppatore disabilitare CsrfViewMiddleware
, tutte le view rilevanti nelle app contrib usano il decoratore csrf_protect
per assicurare la sicurezza di queste applicazioni contro CSRF. Si raccomanda agli sviluppatori di applicazioni riutilizzabili che vogliano le stesse garanzie di usare anche il decoratore csrf_protect
sulle proprie view.