Les jetons web JSON (JWT) sont devenus la norme pour l'authentification sans état dans les applications web et mobiles modernes. Ils offrent un moyen concis et autonome de transmettre des revendications entre les parties. Cependant, cette commodité conduit souvent à un faux sentiment de sécurité. De nombreux développeurs implémentent JWT sans comprendre pleinement les vecteurs d'attaque potentiels, tels que le vol de jeton, les attaques par rejeu et la confusion d'algorithmes. Dans ce guide, nous irons au-delà de l'implémentation de base et explorerons les mesures de sécurité critiques nécessaires pour protéger votre couche d'authentification.
1. Ne stockez jamais les JWT dans le localStorage
L'erreur la plus courante dans l'implémentation des JWT consiste à stocker les jetons dans le localStorage ou le sessionStorage du navigateur. Bien qu'accessibles, ces mécanismes de stockage sont vulnérables aux attaques par script intersite (XSS). Si un attaquant injecte du code JavaScript malveillant dans votre application, il peut facilement lire le jeton dans le localStorage et usurper l'identité de l'utilisateur.
La solution : Stockez les JWT dans des cookies httpOnly, Secure et SameSite=Strict. Cela garantit que le jeton n'est envoyé qu'avec les requêtes adressées au même domaine et est inaccessible au JavaScript côté client. Cela atténue efficacement le vol de jetons basé sur XSS.
2. Implémentez des jetons d'accès à courte durée de vie
Les jetons d'accès à longue durée de vie sont une bombe à retardement. Si un jeton d'accès est volé, l'attaquant dispose d'une longue fenêtre pour l'exploiter. En réduisant la durée de vie d'un jeton d'accès (par exemple, 5 à 15 minutes), vous minimisez les risques associés à son vol.
La solution : Utilisez une stratégie de jeton d'actualisation (Refresh Token). Les jetons d'accès doivent avoir une courte durée de vie, tandis que les jetons d'actualisation peuvent avoir une durée de vie plus longue (par exemple, 7 jours). Le jeton d'actualisation est stocké en toute sécurité dans un cookie, et lorsque le jeton d'accès expire, le client utilise le jeton d'actualisation pour obtenir un nouveau jeton d'accès sans que l'utilisateur ait besoin de se reconnecter.
Exemple : Définition de l'expiration du jeton dans Node.js/Express
const jwt = require('jsonwebtoken');
// Jeton d'accès à courte durée de vie
const accessToken = jwt.sign(
{ userId: user.id, role: user.role },
process.env.ACCESS_TOKEN_SECRET,
{ expiresIn: '15m' }
);
// Jeton d'actualisation à longue durée de vie (à stocker en toute sécurité dans la BDD et le cookie)
const refreshToken = jwt.sign(
{ userId: user.id },
process.env.REFRESH_TOKEN_SECRET,
{ expiresIn: '7d' }
);
3. Utilisez des algorithmes de signature robustes
Les JWT reposent sur la signature cryptographique pour garantir l'intégrité. Les développeurs doivent s'assurer d'utiliser des algorithmes asymétriques tels que RS256 ou ES256, ou des algorithmes symétriques forts tels que HS256 avec des secrets complexes. Évitez d'utiliser l'algorithme none, qui désactive entièrement la vérification, et soyez prudent face aux attaques de confusion d'algorithmes où un attaquant modifie l'algorithme de RS256 vers HS256 pour falsifier des jetons.
La solution : Définissez explicitement les algorithmes autorisés dans votre logique de vérification. Ne faites jamais confiance à l'en-tête alg envoyé par le client.
Exemple : Vérification sécurisée des JWT
// Dans Node.js avec jsonwebtoken
const verified = jwt.verify(token, publicKey, {
algorithms: ['RS256'] // Autoriser explicitement uniquement RS256
});
4. Implémentez la révocation des jetons
L'une des principales critiques adressées aux JWT est qu'ils sont sans état, ce qui rend leur révocation difficile. Cependant, pour les applications critiques sur le plan de la sécurité, vous devez être capable de révoquer les jetons, en particulier si un utilisateur se déconnecte ou si son compte est compromis.
La solution : Maintenez une liste noire des identifiants de jetons révoqués (JTI - JWT ID). Lorsqu'un utilisateur se déconnecte ou qu'un incident de sécurité se produit, ajoutez le JTI du jeton à un magasin à accès rapide comme Redis, avec une durée d'expiration correspondant à la durée de vie restante du jeton. Vérifiez le JTI par rapport à cette liste noire lors de la validation de la requête.
Conclusion
Sécuriser l'authentification JWT ne consiste pas seulement à signer un jeton ; cela nécessite une approche holistique impliquant un stockage sécurisé, des durées de vie courtes, des pratiques de signature robustes et des mécanismes de révocation. En suivant ces meilleures pratiques, vous pouvez réduire considérablement la surface d'attaque de votre application et offrir une expérience plus sûre à vos utilisateurs. Rappelez-vous que la sécurité est un processus continu, donc auditez régulièrement votre flux d'authentification et tenez-vous informé des dernières normes cryptographiques.