Skip to content

Chapitre 5.4 : Travailler avec les jetons CSRF

Temps d'étude : 30 minutes


1. Qu'est-ce qu'une attaque CSRF ? Le "détournement" de votre vaisseau

Imaginez que vous êtes connecté au panneau de contrôle de votre flotte spatiale (space-api.test). Dans un onglet voisin, vous ouvrez un site de chats inoffensif (evil-cats.com). Sur ce site, il y a un formulaire caché qui envoie automatiquement une requête à votre site à l'adresse POST /api/planets/1/delete.

Étant donné que vous êtes déjà autorisé sur space-api.test, votre navigateur attachera gentiment tous vos cookies à cette requête. Le serveur Laravel verra une session valide et pensera que c'est vous qui avez décidé de déclasser la planète. La planète sera supprimée à votre insu.

C'est ce qu'on appelle la CSRF (Cross-Site Request Forgery) — une attaque où un attaquant force le navigateur d'un utilisateur authentifié à effectuer une action indésirable sur un site de confiance.

💡 Analogie spatiale :

Vous êtes le capitaine d'un vaisseau, et vous avez une carte-clé (session/cookie). L'attaquant ne peut pas voler votre carte. Mais il peut vous forcer par la ruse à l'appliquer au terminal de déclassification des ressources pendant que vous êtes distrait. Un jeton CSRF, c'est comme un code PIN que vous devez saisir avec la carte. L'attaquant ne connaît pas le code PIN, et son attaque échoue.


2. Comment Laravel protège-t-il contre la CSRF ?

Laravel protège par défaut toutes les requêtes web "non sécurisées" (POST, PUT, PATCH, DELETE) à l'aide d'un jeton CSRF.

  1. Lors de la génération d'une page, Laravel crée un jeton unique et aléatoire pour la session de l'utilisateur.
  2. Ce jeton est intégré dans les formulaires HTML.
  3. Lors de l'envoi du formulaire, le jeton est envoyé avec la requête.
  4. Côté serveur, le middleware VerifyCsrfToken compare le jeton de la requête avec le jeton stocké dans la session.
  5. Si les jetons ne correspondent pas, Laravel interrompt la requête avec une erreur 419 (Session expirée/Page expirée).

Important : Les routes API dans routes/api.php ne sont pas protégées par CSRF, car elles supposent un autre mécanisme d'authentification (par exemple, les jetons Sanctum), et non des sessions basées sur les cookies. Notre problème actuel concerne spécifiquement les routes web et les pages que nous créons dans routes/web.php.


3. Utilisation du jeton CSRF dans les formulaires HTML

C'est le scénario le plus simple. Laravel fournit une directive Blade spéciale pour cela.

Exemple : Formulaire de création de planète Créons un formulaire simple dans le fichier resources/views/planets/create.blade.php :

<h2>Formulaire de lancement d'une nouvelle planète</h2>
<form action="/planets" method="POST">
    @csrf {{-- Voilà la magie ! --}}

    <label for="name">Nom :</label>
    <input type="text" id="name" name="name" required>

    <label for="solar_system">Système solaire :</label>
    <input type="text" id="solar_system" name="solar_system" required>

    {{-- ... autres champs ... --}}

    <button type="submit">Lancer</button>
</form>

La directive @csrf générera automatiquement un champ caché dans le formulaire :

<input type="hidden" name="_token" value="j2aK3dLf4gH5...jeton_unique...">

C'est suffisant pour protéger les formulaires HTML standards.


4. Utilisation du jeton CSRF dans les requêtes AJAX/Fetch

Dans le chapitre précédent, nous avons envoyé une requête DELETE en utilisant JavaScript. Maintenant, Laravel va la bloquer avec une erreur 419. Nous devons ajouter le jeton CSRF dans les en-têtes de notre requête Fetch.

Étape 1 : Rendre le jeton accessible à JavaScript

Ajoutez une balise meta avec le jeton dans le <head> de votre mise en page principale resources/views/app.blade.php. C'est une pratique standard dans Laravel.

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    {{-- Ajout du jeton CSRF dans la balise meta --}}
    <meta name="csrf-token" content="{{ csrf_token() }}">

    {{-- ... --}}
</head>

La fonction csrf_token() renvoie le jeton actuel.

Étape 2 : Modifier JavaScript pour envoyer le jeton

Maintenant, dans notre public/js/planets.js, nous pouvons lire ce jeton et l'ajouter aux en-têtes de toutes les requêtes "non sécurisées".

// ... dans le fichier public/js/planets.js ...

document.addEventListener('DOMContentLoaded', () => {
    // Obtenir le jeton de la balise meta
    const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');

    const deleteButtons = document.querySelectorAll('.delete-btn');

    deleteButtons.forEach(button => {
        button.addEventListener('click', async (event) => {
            // ... logique de confirmation ...

            try {
                const response = await fetch(apiUrl, {
                    method: 'DELETE',
                    headers: {
                        'Accept': 'application/json',
                        'X-CSRF-TOKEN': csrfToken // <-- Ajouter le jeton aux en-têtes !
                    }
                });

                // ... reste de la logique de traitement de la réponse ...
            } catch (error) {
                // ...
            }
        });
    });
});
  • Le nom de l'en-tête X-CSRF-TOKEN est la norme que Laravel vérifie par défaut.

Nos requêtes AJAX sont maintenant également protégées. Essayez à nouveau de supprimer une planète — cette fois, la requête sera réussie.


Quiz pour la consolidation

1. Quelle attaque le jeton CSRF prévient-il ?

2. Quelle directive Blade ajoute un champ caché avec le jeton CSRF à un formulaire ?

3. Que se passe-t-il si vous envoyez une requête POST sans jeton CSRF à une route web ?

4. Quel en-tête HTTP standard est utilisé pour envoyer le jeton CSRF dans les requêtes AJAX ?

5. Pourquoi les routes API (`routes/api.php`) n'utilisent-elles pas la protection CSRF par défaut ?


🚀 Résumé du chapitre :

Vous avez installé un "système d'identification ami ou ennemi" sur votre vaisseau spatial, le protégeant ainsi des attaques CSRF. Vous avez appris à :

  • Comprendre l'essence et le danger des attaques CSRF.
  • Protéger les formulaires HTML standards à l'aide de la directive @csrf.
  • Transmettre le jeton CSRF à JavaScript via une balise meta.
  • Inclure le jeton dans les en-têtes des requêtes AJAX/Fetch pour leur exécution réussie.

Vos interfaces web sont maintenant non seulement interactives, mais aussi sécurisées. Dans le chapitre suivant, nous terminerons la création de notre interface web en examinant comment organiser correctement le routage pour les pages web.