Comment utiliser la protection CSRF de Django

Pour profiter de la protection CSRF dans les vues, procédez comme suit :

  1. L’intergiciel CSRF est activé par défaut dans le réglage MIDDLEWARE. Si vous surchargez ce réglage, rappelez-vous que 'django.middleware.csrf.CsrfViewMiddleware' doit figurer avant les intergiciels qui comptent sur le fait que les attaques CSRF ont déjà été contrées.

    Si vous le désactivez, ce qui n’est pas recommandé, vous pouvez utiliser csrf_protect() sur certaines vues que vous souhaitez protéger (voir ci-dessous).

  2. Dans tout gabarit qui utilise un formulaire POST, utilisez la balise de gabarit csrf_token à l’intérieur de la balise HTML <form> si le formulaire renvoie vers une URL interne, par exemple :

    <form method="post">{% csrf_token %}
    

    Par contre, il ne faut pas le faire pour les formulaires POST qui ciblent des URL externes, car cela entraînerait la divulgation du jeton CSRF, et conduirait ainsi à une vulnérabilité.

  3. Dans les fonctions de vue correspondantes, vérifiez que RequestContext est utilisé pour produire la réponse afin que {% csrf_token %} fonctionne correctement. Si vous utilisez la fonction render(), les vues génériques ou les applications contribuées, il n’y a pas de soucis à se faire car toutes ces vues utilisent RequestContext.

Utilisation de la protection CSRF avec AJAX

Même si la méthode ci-dessus peut être utilisée pour les requêtes AJAX POST, elle présente quelques inconvénients : il ne faut pas oublier de passer le jeton CSRF en même temps que les données POST à ​​chaque requête POST. Pour cette raison, il y a une autre méthode : sur chaque XMLHttpRequest, définissez un élément d’en-tête X-CSRFToken (tel qu’indiqué par le réglage CSRF_HEADER_NAME) ayant la même valeur que le jeton CSRF. C’est souvent plus simple, parce que de nombreuses bibliothèques JavaScript fournissent des points d’extension qui permettent d’ajouter des éléments d’en-tête pour chaque requête.

Il faut d’abord obtenir le jeton CSRF. La façon de faire dépend de l’activation ou non des réglages CSRF_USE_SESSIONS et CSRF_COOKIE_HTTPONLY.

Définition du jeton pour une requête AJAX

Enfin, vous devrez renseigner l’en-tête de votre requête AJAX. En utilisant 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) {
    // ...
});

Utilisation de la protection CSRF dans les gabarits Jinja2

Le moteur de gabarit Jinja2 de Django ajoute {{ csrf_input }} au contexte de tous les gabarits, ce qui est équivalent à {% csrf_token %} dans le langage de gabarit de Django. Par exemple :

<form method="post">{{ csrf_input }}

La méthode du décorateur

Plutôt que d’ajouter CsrfViewMiddleware comme une protection générale, vous pouvez utiliser le décorateur csrf_protect(), qui offre exactement la même fonctionnalité, sur les vues particulières qui ont besoin de la protection. Ce décorateur doit être utilisé à la fois sur les vues qui insèrent le jeton CSRF dans leur rendu, et sur celles qui acceptent les données de formulaire POST (il s’agit souvent de la même fonction de vue, mais pas toujours).

Le recours à l’utilisation seule du décorateur n’est pas recommandé, car si vous oubliez de l’utiliser, vous aurez un trou de sécurité. La stratégie « ceinture et bretelles » qui consiste à utiliser les deux convient tout à fait et n’entraînera qu’une surcharge minimale.

Gestion des requêtes rejetées

Par défaut, une réponse de type « 403 Forbidden » est renvoyée à l’utilisateur si une requête entrante ne satisfait pas les contrôles effectués par CsrfViewMiddleware. Ça ne devrait généralement apparaître que lorsqu’il s’agit d’une véritable attaque de type « Cross Site Request Forgery », ou lorsqu’en raison d’une erreur de programmation, le jeton CSRF n’a pas été inclus dans un formulaire de type POST.

La page d’erreur n’est toutefois pas très sympathique, de sorte que vous pouvez fournir votre propre vue pour traiter cette condition. Pour ce faire, définissez le réglage CSRF_FAILURE_VIEW.

Les échecs CSRF sont journalisés comme avertissements dans le journaliseur django.security.csrf.

Utilisation de la protection CSRF avec le cache

Si la balise de gabarit csrf_token est utilisée par un gabarit (ou que la fonction get_token est appelée d’une autre façon), l’intergiciel CsrfViewMiddleware ajoute un cookie et un en-tête Vary: Cookie à la réponse. Cela signifie que l’intergiciel fonctionnera bien avec l’intergiciel de cache s’il est utilisé conformément aux instructions (UpdateCacheMiddleware doit être placé dans les réglages avant tout autre intergiciel).

Toutefois, si vous utilisez des décorateurs de cache sur des vues individuelles, l’intergiciel CSRF n’aura pas encore pu définir l’en-tête Vary ou le cookie CSRF, et la réponse sera mise en cache sans l’un ni l’autre. Dans ce cas, sur les vues qui nécessitent qu’un jeton CSRF soit inséré, vous devez utiliser le décorateur de fonction django.views.decorators.csrf.csrf_protect() en premier :

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): ...

Si vous utilisez des vues fondées sur les classes, vous pouvez vous référer à la Décoration des vues fondées sur les classes.

La protection CSRF dans les tests

Généralement, CsrfViewMiddleware gêne les tests des fonctions de vues, parce que le jeton CSRF doit être envoyé avec chaque requête POST. Le client HTTP de Django utilisé pour les tests a donc été modifié afin de marquer automatiquement les requêtes et ainsi d’informer l’intergiciel et le décorateur csrf_protect de manière à ce qu’ils ne rejettent pas ces requêtes. À tous les autres égards (par exemple, l’envoi des cookies, etc.), le comportement durant les tests est identique.

Si pour une raison quelconque vous voulez que le client HTTP utilisé pour les tests effectue des contrôles CSRF, vous pouvez créer une instance du client de test qui applique les vérifications CSRF :

>>> from django.test import Client
>>> csrf_client = Client(enforce_csrf_checks=True)

Cas particuliers

Certaines vues peuvent avoir des exigences inhabituelles qui impliquent qu’elles ne correspondent pas au modèle normal envisagé ici. Un certain nombre d’utilitaires peuvent être utiles dans ces situations. Les scénarios qui pourraient alors être nécessaires sont décrits dans la section suivante.

Désactivation de la protection CSRF pour quelques vues choisies

La plupart des vues nécessitent une protection CSRF, mais quelques-unes n’en ont pas besoin.

Solution : plutôt que de désactiver l’intergiciel et d’appliquer csrf_protect à toutes les vues qui en ont besoin, activez l’intergiciel et utilisez csrf_exempt().

Définition du jeton lorsque CsrfViewMiddleware.process_view() n’est pas utilisée

Il y a des cas où CsrfViewMiddleware.process_view n’aura pas été exécuté avant votre vue - les gestionnaires d’erreurs 404 et 500, par exemple - mais vous avez quand même besoin du jeton CSRF dans un formulaire.

Solution : utilisez requires_csrf_token()

Inclusion du jeton CSRF dans une vue non protégée

Certaines vues peuvent ne pas être protégées, peut-être exemptées par csrf_exempt, mais ont tout de même besoin d’inclure le jeton CSRF.

Solution : utilisez csrf_exempt() suivi de requires_csrf_token() (c’est à dire que requires_csrf_token doit être le décorateur le plus à l’intérieur).

Protection d’une vue pour un seul chemin

Une vue a besoin de protection CSRF seulement quand un certain nombre de conditions sont remplies, et ne doit pas être protégée le reste du temps.

Solution : utilisez csrf_exempt() pour l’ensemble de la fonction de vue, et csrf_protect() spécifiquement pour le chemin qui doit être protégé. Exemple :

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()

Protection d’une page utilisant AJAX sans formulaire HTML

Une page effectue une requête POST via AJAX, et cette page n’a pas de formulaire HTML avec une balise csrf_token qui provoquerait la transmission du cookie CSRF.

Solution : utilisez ensure_csrf_cookie() sur la vue qui envoie la page.

La protection CSRF dans les applications réutilisables

Comme il est possible pour le développeur de désactiver l’intergiciel CsrfViewMiddleware, toutes les vues concernées dans les applications « contrib » de Django utilisent le décorateur csrf_protect pour garantir la protection de ces applications contre les attaques CSRF. Il est recommandé que les développeurs d’autres applications réutilisables qui souhaitent offrir les mêmes garanties utilisent également le décorateur csrf_protect sur leurs vues.

Back to Top