← Journal technique

security

CSRF, et quand une config JWT en a discrètement besoin quand même

CSRF, et quand une config JWT en a discrètement besoin quand même

Un site malveillant piège le navigateur d'un utilisateur connecté pour qu'il fasse une requête vers ton application, et le navigateur attache automatiquement les cookies de l'utilisateur — parce que toute requête vers tonapp.com porte les cookies de tonapp.com peu importe la page qui l'a initiée. Si l'application fait confiance à « a un cookie de session valide » comme preuve d'intention, un attaquant peut faire agir le navigateur en tant que l'utilisateur — changer un e-mail, soumettre un formulaire — sans que l'utilisateur ne fasse sciemment quoi que ce soit.

Ça ne compte que pour l'authentification par cookie. Une API qui exige strictement un JWT dans un en-tête Authorization en est majoritairement à l'abri — une requête falsifiée ne peut pas attacher une valeur d'en-tête que l'attaquant ne connaît pas, et la politique de même origine empêche de lire le localStorage d'une autre origine.

Le point qui piège les gens

J'avais quand même besoin de protection CSRF, parce que les jetons de rafraîchissement sont des cookies httpOnly. C'est une bonne pratique — ça les garde hors de portée du XSS — mais ça réintroduit le problème des cookies : le navigateur envoie aussi ce cookie avec une requête cross-site falsifiée. Tout endpoint atteignable via une requête porteuse de cookie a besoin de protection.

Les défenses standard, en couches :

  • Un jeton double-submit ou synchroniseur. Le serveur émet un jeton CSRF que le front-end doit renvoyer dans un en-tête personnalisé sur les requêtes qui changent l'état, qu'une requête cross-site falsifiée ne peut pas lire ni rejouer.
  • SameSite=Lax ou Strict sur le cookie lui-même, disant au navigateur de ne pas l'envoyer du tout sur les requêtes cross-site. Lax autorise encore la navigation de premier niveau comme un lien cliqué ; Strict est plus serré mais peut casser des flux légitimes comme un retour de redirection de paiement.
  • Vérifier les en-têtes Origin/Referer comme un contrôle secondaire plus léger — pas étanche seul, puisque certains proxys et extensions retirent des en-têtes, mais une couche de plus.

Le travail CSRF dans l'historique de ce projet a atterri à côté de correctifs CI et CodeQL, ce qui n'est pas une coïncidence — CodeQL est exactement le genre d'analyse statique qui signale une vérification CSRF manquante sur un endpoint authentifié par cookie qui change l'état.