Imaginez que quelqu'un pousse un visiteur à cliquer sur un lien pendant qu'il est connecté à son compte bancaire. Ce lien mène à un formulaire caché qui, à l'insu du visiteur, envoie à la banque une demande de virement. La banque voit une demande légitime venant d'un utilisateur authentifié et l'exécute. L'attaquant a déplacé de l'argent depuis le compte de la victime sans jamais connaître son mot de passe.
C'est ce qu'on appelle une attaque CSRF (Cross-Site Request Forgery) : l'attaquant pousse une victime à envoyer, sans le savoir, un formulaire ou une requête vers un autre site où elle est déjà connectée.
Ce que vous allez apprendre : ce que sont les attaques CSRF et comment elles fonctionnent, pourquoi elles restent dangereuses même quand d'autres mesures de sécurité sont en place, comment les jetons CSRF les empêchent et comment protéger vos formulaires.
Qu'est-ce que le CSRF ?
CSRF (qui se prononce « sea-surf ») signifie Cross-Site Request Forgery, c'est-à-dire falsification de requête intersite. Dans cette attaque, l'attaquant exploite un fait simple : quand vous êtes connecté à un site, votre navigateur fait confiance à votre session et l'inclut automatiquement dans chaque requête vers ce site, même si la requête provient d'un autre site.
L'attaque consiste à pousser un visiteur connecté à envoyer, sans le savoir, un formulaire vers un site où il a un compte. L'attaquant ne voit pas le mot de passe de la victime. Il n'en a pas besoin. Il lui suffit que la victime soit déjà connectée, puis de l'amener à faire une requête (comme envoyer un formulaire caché) qui produit l'effet voulu.
Le CSRF diffère des autres attaques, car il n'exige ni vol d'identifiants ni cassage de chiffrement. Il exploite la confiance de la victime envers le site. La victime est déjà authentifiée, donc le site fait confiance aux requêtes qui semblent venir d'elle.
Comment fonctionnent les attaques CSRF
Une attaque CSRF se déroule en trois étapes.
Étape 1 : la victime est connectée à votre site
Quelqu'un visite votre site et se connecte à son compte. Son navigateur détient alors un cookie de session actif qui prouve son authentification. Cette session reste active tant qu'elle continue d'utiliser le site.
Étape 2 : l'attaquant l'amène sur une page malveillante
Toujours connectée à votre site, la victime visite une page contrôlée par l'attaquant (un lien sur les réseaux sociaux, une publicité trompeuse ou un site d'apparence légitime). Sur cette page se trouve un code caché qui envoie un formulaire vers votre site.
Étape 3 : le formulaire s'envoie tout seul avec la session de la victime
Le formulaire caché est envoyé à votre site. Comme la victime est déjà connectée, son navigateur ajoute automatiquement son cookie de session à la requête. Du point de vue de votre formulaire, c'est une requête légitime d'un utilisateur authentifié. Il la traite comme si la victime l'avait envoyée volontairement.
L'attaquant a maintenant fait quelque chose sur votre site avec le compte de la victime, sans qu'elle le sache. Il a pu changer son mot de passe, faire un virement, modifier son profil ou envoyer des données.
Pourquoi l'attaque fonctionne malgré d'autres mesures de sécurité
Les attaques CSRF contournent plusieurs protections courantes :
HTTPS n'arrête pas le CSRF. Les données sont chiffrées pendant le transport, mais l'attaquant n'a pas besoin de les intercepter. Il envoie simplement la requête depuis le navigateur de la victime, auquel ce navigateur fait déjà confiance.
La protection par mot de passe n'arrête pas le CSRF. L'attaquant n'a jamais besoin du mot de passe de la victime. Il exploite le fait qu'elle est déjà connectée.
Un système de connexion solide n'arrête pas le CSRF. Même si l'authentification est parfaite, dès que la victime est connectée, l'attaque fonctionne.
C'est pourquoi la protection CSRF forme une couche à part. Les autres mesures de sécurité ne l'assurent pas. Il faut concevoir vos formulaires pour résister spécifiquement à ces attaques.
Les jetons CSRF : comment ils bloquent l'attaque
La solution est un jeton CSRF. C'est une chaîne unique et imprévisible que votre serveur génère pour chaque formulaire. Voici comment cela marche :
Votre serveur crée un jeton unique pour chaque visiteur
Quand quelqu'un charge une page contenant un formulaire, votre serveur génère un jeton aléatoire et l'associe à la session de cet utilisateur. Ce jeton est différent à chaque fois et propre à cet utilisateur.
Le jeton est intégré au formulaire
Le jeton est inclus dans le formulaire HTML sous forme de champ caché. Quand le visiteur envoie le formulaire, le jeton part avec.
Votre serveur vérifie le jeton avant de traiter l'envoi
À la réception, votre serveur vérifie que le jeton reçu correspond à celui généré pour la session de cet utilisateur. Si oui, le formulaire est traité. Si non (ou s'il n'y a pas de jeton), il est rejeté.
Le formulaire caché de l'attaquant ne peut pas contenir de jeton valide, car les jetons sont imprévisibles et propres à chaque session. Un attaquant sur un autre domaine ne peut ni voir ni deviner le jeton. Son attaque échoue donc. Le formulaire caché est envoyé, mais votre serveur le rejette parce que le jeton ne correspond pas.
À quoi ressemble un jeton CSRF en pratique
En HTML, un formulaire protégé contre le CSRF ressemble à ceci :
<form method="POST" action="/submit-form"> <input type="hidden" name="csrf_token" value="a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"> <input type="email" name="email" placeholder="Your email"> <button type="submit">Submit</button> </form> Le champ `csrf_token` est caché. Le visiteur ne le voit pas, mais il est envoyé avec les données du formulaire. Votre serveur le vérifie avant de traiter l'e-mail ou toute autre donnée.
La protection CSRF selon le type de formulaire
Formulaires HTML classiques
Les formulaires HTML standard (ceux qui envoient les données à votre serveur en POST) ont besoin de jetons CSRF. Cela inclut les formulaires de contact, de connexion, de réinitialisation de mot de passe et tout formulaire qui modifie des données sur votre serveur.
Formulaires AJAX et JavaScript
Les formulaires envoyés en JavaScript vers une API ont eux aussi besoin d'une protection CSRF. Le jeton peut être intégré au formulaire, stocké dans un cookie ou récupéré dans une balise meta avant l'envoi de la requête.
Requêtes d'API sans état
Si votre API utilise une authentification par jeton (comme JWT), la protection CSRF fonctionne autrement. Le navigateur ne peut pas joindre automatiquement un jeton JWT comme il joint un cookie de session, donc le CSRF classique est une menace moindre. La bonne pratique reste pourtant d'ajouter un jeton CSRF pour plus de sécurité.
D'autres protections qui complètent les jetons CSRF
Les jetons CSRF sont la défense principale, mais ils fonctionnent mieux avec d'autres bonnes pratiques de sécurité.
Cookies SameSite
Quand vous définissez un cookie de session, vous pouvez lui ajouter un attribut `SameSite` qui indique au navigateur de ne pas l'envoyer quand la requête vient d'un autre domaine. Le formulaire caché de l'attaquant ne peut alors plus du tout utiliser la session de la victime.
Vérification de l'en-tête Referer
Votre serveur peut vérifier l'en-tête Referer pour savoir d'où vient la requête. Si un envoi prétend venir de votre domaine alors que le Referer indique un autre domaine, c'est probablement une attaque. (Cependant, les en-têtes Referer peuvent être falsifiés : ce n'est donc pas une défense complète.)
Cookie à double envoi
Dans cette approche, le jeton CSRF est stocké à la fois dans un cookie et dans un champ du formulaire. Votre serveur vérifie qu'ils correspondent avant de traiter la requête. C'est moins courant que les jetons classiques, mais cela convient bien aux applications sans état.
Comment mettre en place la protection CSRF
Les détails dépendent de votre technologie, mais le schéma est toujours le même :
- Générer un jeton unique par session. Quand un utilisateur démarre une session ou charge une page de formulaire, votre serveur génère un nouveau jeton.
- Stocker le jeton côté serveur. Enregistrez-le en l'associant à la session de cet utilisateur.
- Inclure le jeton dans le formulaire. Ajoutez-le comme champ caché dans le HTML ou comme balise meta que JavaScript peut lire.
- Valider à l'envoi. Avant de traiter les données, vérifiez que le jeton reçu correspond à celui enregistré pour cette session.
- Régénérer après une action sensible. Quand un utilisateur change son mot de passe ou fait une action critique, générez un nouveau jeton.
La plupart des frameworks modernes gèrent automatiquement la génération et la validation des jetons CSRF. Si vous utilisez un framework, la protection est généralement activée par défaut. Veillez simplement à ne pas la désactiver par accident.
Les erreurs courantes qui laissent les formulaires vulnérables
Désactiver la protection CSRF par commodité. Certains développeurs coupent les contrôles CSRF pour faciliter les tests d'API ou simplifier leur code. Ne le faites pas. Cela crée une vraie faille.
Utiliser un jeton statique pour tous les utilisateurs. Le jeton doit être propre à la session de chaque utilisateur. Un jeton identique pour tout le monde (ou identique pour tous les utilisateurs, tous les jours) ne protège pas du CSRF.
Ne pas régénérer les jetons. Si vous gardez le même jeton pendant des semaines, un attaquant a plus de temps pour l'exploiter. Régénérez les jetons régulièrement, et toujours après une opération sensible.
Ne compter que sur les cookies SameSite. SameSite est utile mais insuffisant. Faites des jetons CSRF votre défense principale et de SameSite un renfort.
Oublier le jeton sur certains formulaires. Si vous protégez certains formulaires et pas d'autres, les attaquants passeront par ceux qui ne le sont pas. Tout formulaire qui modifie des données doit être protégé.
Ce que fait WEMASY pour la protection CSRF
Les formulaires WEMASY incluent automatiquement une protection par jeton CSRF sur chaque formulaire que vous créez. Vous n'avez rien à configurer ni à y penser. Quand un visiteur envoie un formulaire, WEMASY valide le jeton avant d'enregistrer la réponse. Cette protection est intégrée et activée par défaut.
Découvrez les options de sécurité des formulaires sur la page des tarifs.