jwtsecurityauthentication

Meilleures pratiques de sécurité JWT pour 2026

· Cosyslabs

Les failles de sécurité JWT se trouvent systématiquement parmi les principales vulnérabilités des API. Utilisez les algorithmes RS256 ou ES256, rejetez explicitement l'algorithme none, définissez l'expiration du token d'accès à moins de 15 minutes, implémentez la rotation des tokens de rafraîchissement et vérifiez toujours les signatures côté serveur avant de faire confiance à un claim.

Qu'est-ce qu'un JWT ?

Un JSON Web Token est composé de trois segments encodés en Base64URL séparés par des points :

en-tête.payload.signature
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.signature
  • En-tête : algorithme et type de token
  • Payload : claims (données utilisateur, expiration, etc.)
  • Signature : preuve cryptographique que le token n'a pas été falsifié

La signature est ce que vous devez vérifier. Un JWT sans signature vérifiée n'est que du JSON non vérifié.

Vulnérabilité critique : Confusion d'algorithme

L'attaque par l'algorithme none

Les premières bibliothèques JWT acceptaient alg: "none" dans l'en-tête, ce qui signifie qu'aucune signature n'était requise. Un attaquant pouvait forger n'importe quel payload :

{
  "alg": "none",
  "typ": "JWT"
}

Sans vérification de signature, il pouvait prétendre être n'importe quel utilisateur. Correction :

// Node.js — spécifiez toujours explicitement les algorithmes autorisés
jwt.verify(token, clefPublique, { algorithms: ["RS256"] });

// Ne jamais autoriser "none"
// Ne jamais utiliser algorithms: ["RS256", "none"] — c'est une vulnérabilité

Attaque par confusion RS256 vs HS256

HS256 (HMAC) utilise un secret partagé — la même clé signe et vérifie. RS256 (RSA) utilise une paire de clés — la clé privée signe, la clé publique vérifie.

L'attaque : si une bibliothèque voit alg: "HS256" et utilise la clé publique RS256 comme secret HMAC, un attaquant qui a obtenu la clé publique (qui est publique !) peut forger des tokens.

Fixez toujours l'algorithme côté serveur. Ne lisez jamais alg depuis le token pour décider comment le vérifier.

// Vulnérable — lit alg depuis le token
function vérifier(token, clef) {
  const { alg } = décoderEnTête(token);
  return vérifierAvec(token, clef, alg); // NE JAMAIS faire ça
}

// Sécurisé — l'algorithme est codé en dur côté serveur
function vérifier(token) {
  return jwt.verify(token, CLEF_PUBLIQUE, { algorithms: ["RS256"] });
}

Recommandations d'algorithmes

AlgorithmeTypeCas d'usage
ES256Asymétrique (ECDSA)Meilleur pour les nouveaux projets — petites signatures, rapide
RS256Asymétrique (RSA)Largement supporté, bon pour l'interopérabilité
HS256Symétrique (HMAC)Uniquement quand le secret est vraiment partagé et jamais exposé
noneAucunNe jamais utiliser

Pour les API publiques où plusieurs services vérifient les tokens, utilisez des algorithmes asymétriques (ES256/RS256). La clé privée reste sur le serveur d'authentification ; tous les autres services ne détiennent que la clé publique.

Expiration des tokens et stratégie de rafraîchissement

Les tokens d'accès de courte durée limitent les dégâts en cas de vol. Utilisez un schéma à deux tokens :

  • Token d'accès : expire en 5–15 minutes, envoyé avec chaque requête API
  • Token de rafraîchissement : expire en 7–30 jours, stocké de manière sécurisée, utilisé uniquement pour obtenir de nouveaux tokens d'accès
// Émettre des tokens
const tokenAccès = jwt.sign(
  { sub: utilisateur.id, role: utilisateur.rôle },
  CLEF_PRIVÉE,
  { algorithm: "ES256", expiresIn: "15m" }
);

const tokenRafraîchissement = jwt.sign(
  { sub: utilisateur.id, jti: crypto.randomUUID() },
  SECRET_RAFRAÎCHISSEMENT,
  { expiresIn: "7d" }
);

Rotation des tokens de rafraîchissement

Chaque fois qu'un token de rafraîchissement est utilisé, invalidez-le et émettez-en un nouveau. Si un token de rafraîchissement volé est détecté comme utilisé deux fois, invalidez toute la session :

async function rafraîchirTokens(ancienTokenRafraîchissement) {
  const payload = jwt.verify(ancienTokenRafraîchissement, SECRET_RAFRAÎCHISSEMENT);
  
  // Vérifier que le token n'a pas été utilisé avant (détection de réutilisation)
  const enregistrementToken = await db.tokensRafraîchissement.findOne({ jti: payload.jti });
  
  if (!enregistrementToken || enregistrementToken.utilisé) {
    // Réutilisation de token détectée — révoquer toute la famille
    await db.tokensRafraîchissement.révoquerFamille(payload.sub);
    throw new Error("Réutilisation de token détectée");
  }
  
  // Marquer comme utilisé
  await db.tokensRafraîchissement.marquerUtilisé(payload.jti);
  
  // Émettre une nouvelle paire
  return émettrePaireDeTokens(payload.sub);
}

Stockage sécurisé des JWT

StockageRisque XSSRisque CSRFRecommandation
localStorageÉlevéAucunJamais pour les tokens d'authentification
sessionStorageÉlevéAucunJamais pour les tokens d'authentification
Mémoire (var JS)FaibleAucunBon pour les tokens d'accès
Cookie HttpOnlyAucunMoyenMeilleur pour les tokens de rafraîchissement + CSRF

Stockez les tokens d'accès en mémoire JavaScript. Stockez les tokens de rafraîchissement dans des cookies HttpOnly, Secure, SameSite=Strict.

// Définir le token de rafraîchissement comme cookie HttpOnly
res.cookie("refresh_token", tokenRafraîchissement, {
  httpOnly: true,
  secure: true,
  sameSite: "strict",
  maxAge: 7 * 24 * 60 * 60 * 1000, // 7 jours
  path: "/auth/refresh", // envoyé uniquement au endpoint de rafraîchissement
});

Claims à toujours valider

Au-delà de la vérification de la signature, validez ces claims standards :

jwt.verify(token, CLEF_PUBLIQUE, {
  algorithms: ["ES256"],
  issuer: "https://auth.votredomaine.com",    // claim iss
  audience: "https://api.votredomaine.com",   // claim aud
  // exp est vérifié automatiquement
  // nbf est vérifié automatiquement
});
ClaimSignificationToujours valider
expDélai d'expirationOui
nbfPas avantOui
issÉmetteurOui
audAudienceOui
subSujet (ID utilisateur)Oui, correspondre à la session
jtiID JWTOui, pour la révocation

Révocation de JWT

Les JWT sont sans état — un token valide reste valide jusqu'à expiration. Pour une révocation immédiate (déconnexion, changement de mot de passe, suspension de compte), maintenez une liste de blocage :

// À la déconnexion
await redis.setex(`révoqué:${payload.jti}`, secondesTtlToken, "1");

// À chaque requête
async function vérifierToken(token) {
  const payload = jwt.verify(token, CLEF_PUBLIQUE, { algorithms: ["ES256"] });
  
  const estRévoqué = await redis.exists(`révoqué:${payload.jti}`);
  if (estRévoqué) throw new Error("Token révoqué");
  
  return payload;
}

La courte expiration du token d'accès réduit la durée pendant laquelle vous devez maintenir la liste de blocage.

Débogage des JWT

Utilisez l'Outil Décodeur JWT pour inspecter les en-têtes et les payloads sans envoyer des tokens à des services externes. Tout le décodage se passe dans votre navigateur.

Liste de contrôle

  • Utiliser ES256 ou RS256 — jamais HS256 pour les API publiques
  • Rejeter explicitement alg: "none" dans la configuration de la bibliothèque
  • Fixer l'algorithme côté serveur — ne jamais le lire depuis l'en-tête du token
  • Définir l'expiration du token d'accès à 5–15 minutes
  • Implémenter la rotation des tokens de rafraîchissement avec détection de réutilisation
  • Stocker les tokens de rafraîchissement dans des cookies HttpOnly, les tokens d'accès en mémoire
  • Valider iss, aud, exp, nbf à chaque requête
  • Implémenter la révocation basée sur JTI pour la déconnexion/changement de mot de passe
  • Ne jamais journaliser des JWT complets — ce sont des identifiants porteurs

Plus d'outils de Cosyslabs

  • Rough Estimator — Estimez l'effort de développement pour implémenter l'authentification JWT, la rotation des tokens de rafraîchissement et des couches API sécurisées.
  • Routine Toolkit — Utilitaires du quotidien incluant une calculatrice de prêt, une calculatrice de dates et un compteur de mots.
  • Cosyslabs — Le studio derrière Dev Tools !, PDF Convert All, Unit Convert All, et plus.