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 funzione render()
, view generiche o app contrib, se già coperto perchè usano già tutte RequestContext
.
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
.
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()
.
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>
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) {
// ...
});
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 }}
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.
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.
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.
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)
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.
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()
.
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()
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).
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()
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.
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.
mag 03, 2024