logo oujood
🔍

La Protection CSRF dans Django

Django exige un jeton secret pour chaque formulaire qui modifie des données, afin qu'un site tiers ne puisse pas soumettre de requêtes au nom d'un utilisateur connecté à son insu.

Django intègre par défaut un rempart contre les attaques CSRF (Cross-Site Request Forgery). Si vous avez déjà vu une erreur 403 « Forbidden » en soumettant un formulaire, c'est que le mécanisme de protection a bloqué une requête suspecte.


Étape 1 — Comprendre le risque CSRF : qu'est-ce qu'une attaque Cross-Site Request Forgery ?

Une attaque CSRF consiste à faire exécuter une requête à l'insu d'un utilisateur déjà authentifié, en profitant du fait que son navigateur envoie automatiquement ses cookies de session à chaque requête vers le site visé. Un site malveillant peut ainsi déclencher un virement, une suppression de compte ou une modification de mot de passe sans que la victime clique volontairement sur rien de suspect.

Sans protection, le serveur ne peut pas distinguer une requête envoyée depuis votre propre formulaire d'une requête forgée depuis un site tiers — les deux arrivent avec les mêmes cookies. Django empêche cela en exigeant un jeton secret, unique et imprévisible, pour chaque action qui modifie des données (POST, PUT, DELETE). Une requête GET n'est jamais concernée : par convention, elle ne doit modifier aucune donnée.


Étape 2 — Protéger un formulaire HTML avec le jeton {% csrf_token %}

Pour qu'un formulaire soit accepté par le serveur, il faut insérer la balise de template {% csrf_token %} à l'intérieur de la balise <form>. Elle génère un champ caché contenant la valeur du jeton :

  📋 Copier le code

<form action="/traitement/" method="post">
    {% csrf_token %}
    <label for="nom">Votre nom :</label>
    <input type="text" name="nom" id="nom">
    <button type="submit">Valider</button>
</form>

Résultat attendu :

En inspectant le code source de la page dans le navigateur, un champ caché
apparaît juste après l'ouverture du formulaire :
<input type="hidden" name="csrfmiddlewaretoken" value="a1B2c3D4...">
En soumettant le formulaire, la requête est acceptée normalement.
Si vous retirez la balise {% csrf_token %} et soumettez à nouveau,
Django répond par une page 403 "CSRF verification failed. Request aborted."

Le middleware CsrfViewMiddleware vérifie systématiquement la présence et la validité de ce jeton à chaque requête POST. S'il est absent ou invalide, Django rejette la requête avant même qu'elle n'atteigne votre vue.

⚠ Erreurs fréquentes derrière un « CSRF verification failed » :

  • La balise {% csrf_token %} a été oubliée dans le formulaire
  • Le formulaire utilise {{ csrf_token }} (double accolades) au lieu de {% csrf_token %} — cela affiche le jeton en texte brut plutôt que de créer le champ caché
  • Le navigateur bloque le cookie csrftoken (mode navigation privée stricte, extension anti-tracking)
  • Le domaine du formulaire ne figure pas dans CSRF_TRUSTED_ORIGINS, requis depuis Django 4.0 pour les requêtes cross-origin

Étape 3 — Envoyer le jeton CSRF dans une requête AJAX

En JavaScript, il n'y a pas de balise de template pour générer le jeton automatiquement : il faut aller le chercher soi-même dans le cookie csrftoken, puis l'ajouter manuellement à l'en-tête HTTP X-CSRFToken de la requête.

  📋 Copier le code

// Lit la valeur d'un cookie par son nom
function getCookie(name) {
    const value = `; ${document.cookie}`;
    const parts = value.split(`; ${name}=`);
    if (parts.length === 2) return parts.pop().split(';').shift();
}

const csrftoken = getCookie('csrftoken');

fetch('/api/commentaires/', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        // Django lit ce header pour valider la requête
        'X-CSRFToken': csrftoken
    },
    body: JSON.stringify({ texte: 'Un commentaire' })
});

Résultat attendu :

Avec l'en-tête X-CSRFToken correctement renseigné, la requête retourne un
statut 200 ou 201 selon la vue. Si l'en-tête est absent ou contient une
valeur incorrecte, la réponse est un statut 403 avec le même message
"CSRF verification failed" que pour un formulaire HTML classique.

Le cookie csrftoken n'existe que si une page contenant {% csrf_token %} ou get_token(request) a déjà été chargée dans la session — sur une API pure sans aucune page HTML, il faut appeler une vue dédiée qui force sa création avant la première requête AJAX.

Pour approfondir le traitement de ces données côté serveur, la section sur les vues Django détaille comment une vue reçoit et valide ce type de requête.


Par carabde | Mis à jour le 18 juillet 2026