Les jetons Web JSON (JWT) sont devenus la norme de facto pour l'authentification sans état dans les applications web modernes. Ils permettent aux serveurs de vérifier l'identité de l'utilisateur sans interroger une base de données à chaque requête. Cependant, leur commodité conduit souvent à des configurations erronées, exposant les applications à de graves vulnérabilités de sécurité. Dans ce guide, nous explorerons des stratégies avancées pour durcir votre implémentation JWT, en allant au-delà de la syntaxe de base vers une architecture de sécurité robuste.
1. Choisissez des algorithmes et des clés robustes
L'une des failles de sécurité les plus critiques dans l'implémentation des JWT est la sélection inappropriée des algorithmes de signature. De nombreux tutoriels recommandent négligemment HS256, qui repose sur une clé symétrique. Bien que cela convienne aux services internes, cela présente des risques dans les systèmes distribués où la clé secrète doit être partagée entre les services. Préférez plutôt des algorithmes asymétriques tels que RS256 ou ES256. Ceux-ci vous permettent de publier une clé publique pour la vérification tout en gardant la clé privée sécurisée sur le serveur.
De plus, n'utilisez jamais de secrets faibles ou vides. Utilisez toujours des chaînes aléatoires à haute entropie générées par un générateur de nombres aléatoires cryptographiquement sûr. Voici un exemple de génération d'une clé privée sécurisée pour RS256 en Node.js :
const crypto = require('crypto');
// Générer une paire de clés RSA de 2048 bits
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem'
}
});
console.log(privateKey); // Stockez ceci en toute sécurité, par ex. dans un coffre-fort
console.log(publicKey); // Distribuez ceci aux clients pour vérification
2. Imposez des durées de vie courtes
Les JWT doivent avoir une durée de vie courte. Un jeton avec une fenêtre d'expiration longue (par exemple, 24 heures ou 30 jours) augmente la fenêtre de risque si un jeton est volé. La meilleure pratique consiste à émettre des jetons d'accès avec des durées de vie courtes (15 à 30 minutes) et à utiliser des jetons de rafraîchissement pour les sessions de longue durée. Les jetons d'accès prouvent l'identité pour les appels API immédiats, tandis que les jetons de rafraîchissement, stockés plus en toute sécurité, sont utilisés pour obtenir de nouveaux jetons d'accès.
const jwt = require('jsonwebtoken');
const generateAccessToken = (user) => {
return jwt.sign(
{ userId: user.id, role: user.role },
privateKey,
{
algorithm: 'RS256',
expiresIn: '15m' // Jeton d'accès à courte durée de vie
}
);
};
const generateRefreshToken = (user) => {
return jwt.sign(
{ userId: user.id },
privateKey,
{
algorithm: 'RS256',
expiresIn: '7d' // Jeton de rafraîchissement à longue durée de vie
}
);
};
3. Implémentez la révocation de jetons
Étant donné que les JWT sont sans état, leur révocation avant expiration n'est pas prise en charge nativement. Pour remédier à cela, vous pouvez mettre en œuvre une liste noire ou une liste de blocage pour les jetons qui ont été explicitement révoqués (par exemple, lors de la déconnexion ou du changement de mot de passe). Pour les jetons à courte durée de vie, cela est moins critique, mais essentiel pour les jetons de rafraîchissement.
Alternativement, utilisez une revendication jti (identifiant de jeton JWT) et stockez les identifiants de jetons émis dans un magasin clé-valeur rapide tel que Redis. Avant de traiter une requête, vérifiez si le jti existe dans le magasin.
4. Stockage sécurisé côté client
L'endroit où vous stockez le JWT sur le client détermine son exposition aux attaques par Script intersite (XSS) et par falsification de requête intersite (CSRF). Stocker les jetons dans localStorage les rend accessibles à tout JavaScript s'exécutant dans le navigateur, facilitant ainsi les attaques XSS. Au lieu de cela, stockez des cookies HTTP-only. Ceux-ci sont inaccessibles au JavaScript, neutralisant efficacement le vol basé sur XSS.
Lors de l'utilisation de cookies, assurez-vous de définir l'attribut SameSite sur Strict ou Lax pour prévenir les attaques CSRF. De plus, transmettez toujours les jetons via HTTPS pour éviter l'interception via des attaques de l'homme du milieu.
Conclusion
Sécuriser l'authentification JWT n'est pas une configuration ponctuelle, mais un processus continu d'évaluation des algorithmes, des durées de vie et des mécanismes de stockage. En mettant en œuvre des algorithmes asymétriques robustes, des durées d'expiration courtes, des stratégies de révocation et des pratiques de stockage sécurisées, vous pouvez réduire considérablement la surface d'attaque de votre application. Rappelez-vous qu'aucune mesure unique n'est infaillible ; la défense en profondeur est la clé pour maintenir une sécurité robuste dans votre flux d'authentification.