Protection contre le « Cross site request forgery » (CSRF)

The CSRF middleware and template tag provides easy-to-use protection against Cross Site Request Forgeries. This type of attack occurs when a malicious website contains a link, a form button or some JavaScript that is intended to perform some action on your website, using the credentials of a logged-in user who visits the malicious site in their browser. A related type of attack, “login CSRF”, where an attacking site tricks a user’s browser into logging into a site with someone else’s credentials, is also covered.

The first defense against CSRF attacks is to ensure that GET requests (and other “safe” methods, as defined by RFC 7231#section-4.2.1) are side effect free. Requests via “unsafe” methods, such as POST, PUT, and DELETE, can then be protected by the steps outlined in How to use Django’s CSRF protection.

Fonctionnement

La protection CSRF est basée sur les éléments suivants :

  1. A CSRF cookie that is a random secret value, which other sites will not have access to.

    CsrfViewMiddleware sends this cookie with the response whenever django.middleware.csrf.get_token() is called. It can also send it in other cases. For security reasons, the value of the secret is changed each time a user logs in.

  2. A hidden form field with the name “csrfmiddlewaretoken”, present in all outgoing POST forms.

    In order to protect against BREACH attacks, the value of this field is not simply the secret. It is scrambled differently with each response using a mask. The mask is generated randomly on every call to get_token(), so the form field value is different each time.

    Cette action est effectuée par la balise de gabarit.

  3. Pour toutes les requêtes entrantes qui n’utilisent pas les méthodes HTTP GET, HEAD, OPTIONS ou TRACE, un cookie CSRF doit être présent, et le champ « csrfmiddlewaretoken » doit être présent et correct. Si ce n’est pas le cas, l’utilisateur obtiendra une erreur 403.

    Lors de la validation de la valeur de champ csrfmiddlewaretoken, seule la valeur secrète, et non pas le jeton complet, est comparée avec la valeur secrète du contenu du cookie. Cela permet d’utiliser des jetons qui changent constamment. Alors que chaque requête peut utiliser son propre jeton, la valeur secrète reste commune à toutes.

    Cette vérification est effectuée par l’intergiciel CsrfViewMiddleware.

  4. CsrfViewMiddleware compare l’en-tête Origin, si fourni par le navigateur, avec l’hôte actuel et le réglage CSRF_TRUSTED_ORIGINS. Cela ajoute une protection contre les attaques de sous-domaines croisés.

  5. De plus, pour les requêtes HTTPS, si l’en-tête Origin n’est pas présent, le CsrfViewMiddleware effectue un contrôle strict du référant. Cela signifie que même si un sous-domaine peut définir ou modifier des cookies pour votre domaine, il ne peut pas forcer un utilisateur à envoyer des données à votre application car cette requête ne viendrait pas de votre domaine exact.

    Cela protège aussi contre une attaque de type « Man-In-The-Middle » qui est possible sous HTTPS lors de l’utilisation d’une valeur secrète indépendante de la session, parce que les en-têtes HTTP Set-Cookie sont (malheureusement) acceptés par les clients, même quand ils parlent à un site en HTTPS. (La vérification du référant n’est pas faite pour les requêtes HTTP car la présence de l’en-tête Referer n’est pas suffisamment fiable sous HTTP.)

    Si le réglage CSRF_COOKIE_DOMAIN est défini, le référant est comparé avec lui. Il est possible d’autoriser les requêtes inter-domaines en incluant un point en premier. Par exemple, CSRF_COOKIE_DOMAIN = '.exemple.com' autorise les requêtes POST depuis www.exemple.com et api.exemple.com. Si le réglage n’est pas défini, le référant doit alors correspondre à l’en-tête HTTP Host.

    L’extension des référants acceptés au-delà de l’hôte courant ou du domaine du cookie peut se faire avec le réglage CSRF_TRUSTED_ORIGINS.

New in Django 4.0:

Le contrôle d”Origin a été ajouté, comme expliqué ci-dessus.

Changed in Django 4.1:

In older versions, the CSRF cookie value was masked.

Cela garantit que seuls les formulaires originaires de domaines de confiance peuvent être utilisés pour renvoyer des données avec une requête POST.

Les requêtes GET sont volontairement ignorées (ainsi que les autres requêtes définies comme « sûres » par la RFC 7231#section-4.2.1). Ces requêtes ne devraivent jamais avoir d’effets secondaires potentiellement dangereux, et de cette manière une attaque CSRF avec une requête GET doit être inoffensive. La RFC 7231#section-4.2.1 définit les méthodes POST, PUT et DELETE comme « non sûres », et toutes les autres méthodes sont aussi supposées être dangereuses, pour que la protection soit maximale.

La protection CRSF ne peut pas protéger contre les attaques de « l’homme du milieu », c’est pourquoi il faut utiliser HTTPS avec Sécurité de transport HTTP stricte (HSTS). Il suppose également la validation de l’en-tête HOST et que le site ne comporte pas de vulnérabilités de scripts inter-sites (parce que ce type de vulnérabilité permet déjà à un attaquant de faire ce qu’une vulnérabilité CSRF permet de faire, et plus encore).

Suppression de l’en-tête Referer

Pour éviter de divulguer l’URL référent à des sites tiers, il peut être souhaitable de désactiver le référent dans les balises <a> de votre site. Par exemple, il est possible d’utiliser la balise <meta name="referrer" content="no-referrer"> ou d’inclure l’en-tête Referrer-Policy: no-referrer. En raison du contrôle strict du référent dans la protection CSRF pour les requêtes HTTPS, ces techniques produisent des échecs CSRF pour les requêtes de méthodes « non sûres ». Il est préférable d’utiliser des alternatives telles que <a rel="noreferrer" ...>" pour les liens vers des sites tiers.

Limitations

Subdomains within a site will be able to set cookies on the client for the whole domain. By setting the cookie and using a corresponding token, subdomains will be able to circumvent the CSRF protection. The only way to avoid this is to ensure that subdomains are controlled by trusted users (or, are at least unable to set cookies). Note that even without CSRF, there are other vulnerabilities, such as session fixation, that make giving subdomains to untrusted parties a bad idea, and these vulnerabilities cannot easily be fixed with current browsers.

Utilitaires

Les exemples ci-dessous s’appliquent à des vues basées sur des fonctions. 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.

csrf_exempt(view)

Ce décorateur marque une vue comme étant exempte de la protection assurée par l’intergiciel. Exemple :

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def my_view(request):
    return HttpResponse('Hello world')
csrf_protect(view)

Décorateur qui fournit la protection similaire à l’intergiciel CsrfViewMiddleware pour une vue.

Utilisation :

from django.shortcuts import render
from django.views.decorators.csrf import csrf_protect

@csrf_protect
def my_view(request):
    c = {}
    # ...
    return render(request, "a_template.html", c)
requires_csrf_token(view)

Normalement, la balise de gabarit csrf_token ne fonctionnera pas si CsrfViewMiddleware.process_view ou un équivalent comme csrf_protect n’a pas été utilisé. Le décorateur de vue requires_csrf_token peut être utilisé pour s’assurer que la balise de gabarit fonctionne. Ce décorateur fonctionne de façon similaire à csrf_protect, mais ne rejette jamais une demande entrante.

Exemple :

from django.shortcuts import render
from django.views.decorators.csrf import requires_csrf_token

@requires_csrf_token
def my_view(request):
    c = {}
    # ...
    return render(request, "a_template.html", c)

Ce décorateur force une vue à envoyer le cookie CSRF.

Réglages

Un certain nombre de réglages peuvent être utilisés pour contrôler le comportement anti-CSRF de Django :

Questions fréquentes

Est-ce que l’envoi d’une paire de jetons CSRF arbitraires (cookie et données POST) est une vulnérabilité ?

Non, délibérément pas. Sans attaque de « l’homme du milieu », il n’existe aucune possibilité pour un attaquant d’envoyer un cookie de jeton CSRF à destination du navigateur de sa victime ; une attaque réussie devrait pouvoir obtenir le cookie du navigateur de la victime par XSS ou autre moyen similaire, auquel cas l’attaquant n’a généralement pas besoin d’attaquer par CSRF.

Certains outils d’audit de sécurité le signalent comme un problème, mais comme mentionné ci-dessus, un attaquant ne peut pas voler un cookie CSRF du navigateur d’un utilisateur. Pouvoir « voler » ou modifier son propre jeton à l’aide de Firebug, des outils de développement Chrome, etc. ne signifie pas qu’il s’agit d’une vulnérabilité.

Est-il problématique que la protection CSRF de Django n’est pas liée à une session par défaut ?

Non, délibérément pas. Le fait de ne pas lier la protection CSRF à une session permet d’utiliser la protection sur des sites tels que pastebin qui autorisent des envois de données par des utilisateurs anonymes et qui n’ont pas de session.

Si vous souhaitez stocker le jeton CSRF dans la session de l’utilisateur, définissez le réglage CSRF_USE_SESSIONS.

Pourquoi un utilisateur peut-il recevoir une erreur de validation CSRF après s’être connecté ?

Pour des raisons de sécurité, les jetons CSRF subissent une rotation lors de chque connexion d’utilisateur. Toute page contenant un formulaire généré avant une connexion possédera un ancien jeton CSRF non valide et devra être rechargée. Cela peut arriver si un utilisateur utilise le bouton « Retour » après une connexion ou s’il se connecte dans un autre onglet du navigateur.

Back to Top