JWT exp/iat/nbf : bugs de réclamation de temps qui brisent l'authentification

JWT — exp, iat et nbf — sont d'une simplicité trompeuse. Ce ne sont que des horodatages Unix. Pourtant, ils provoquent certains des bugs d'authentification les plus frustrants en production : des jetons qui expirent trop tôt, des jetons qui fonctionnent sur un serveur mais pas sur un autre et des jetons qui n'expirent jamais du tout.
Cet article couvre les bogues de réclamation de temps les plus courants, pourquoi ils se produisent et comment les corriger avec des valeurs par défaut sûres.
Comment fonctionnent exp, iat et nbf
Chaque JWT peut contenir trois réclamations liées au temps dans sa charge utile :
- exp (heure d'expiration) — le jeton NE DOIT PAS être accepté après cet horodatage
- iat (émis à) — date à laquelle le jeton a été créé
- nbf (Pas avant) — le jeton NE DOIT PAS être accepté avant cet horodatage
Tous les trois sont des dates numériques : le nombre de secondes depuis 1970-01-01T00:00:00Z (époque Unix). Pas des millisecondes, mais des secondes. Cette distinction à elle seule est à l'origine d'environ 20 % de tous les bugs temporels JWT.
Utilisez notre décodeur JWT pour inspecter instantanément les réclamations de temps dans n'importe quel jeton.
Bug n°1 : utilisation de millisecondes au lieu de secondes
JavaScript Date.now() renvoie des millisecondes. La spécification JWT nécessite quelques secondes. Définir exp sur Date.now() + 3600000 crée un jeton qui expire en 2089, et non dans une heure.
// FAUX — millisecondes
const exp = Date.maintenant() + 3600000 ; // CORRECT — secondes
const exp = Math.floor(Date.now() / 1000) + 3600;La plupart des bibliothèques JWT gèrent cela en interne, mais si vous créez des charges utiles manuellement, c'est la première chose à vérifier.
Bug n°2 : réclamation d'exp manquante entièrement
Si vous oubliez de définir exp, de nombreuses bibliothèques se feront un plaisir de créer un jeton qui n'expire jamais. Il s'agit d'un risque de sécurité : un token divulgué reste valable pour toujours.
Toujours définir exp. Validez-le toujours sur le serveur. Si votre bibliothèque ne rejette pas les jetons sans exp par défaut, configurez-la pour le faire.
// Node.js jsonwebtoken — appliquer l'expiration
jwt.verify(jeton, secret, { maxAge: '1h' });Bogue n°3 : décalage d'horloge entre les serveurs
Le serveur A émet un jeton à 14:00:00. L'horloge du serveur B indique 13:59:55 (5 secondes de retard). Si le jeton a nbf : 1711540800 (14:00:00), le serveur B le rejette comme « pas encore valide ».
Ceci est particulièrement courant dans :
Le correctif
Autoriser une petite tolérance d'horloge (également appelée « marge de manœuvre ») — généralement 30 à 60 secondes :
// jsonwebtoken
jwt.verify(jeton, secret, { clockTolerance: 30 }); // José (Node.js)
attendre jwtVerify(jeton, clé, { clockTolerance: '30s' });Ne réglez jamais une tolérance supérieure à 2 minutes. Si vous en avez besoin de plus, corrigez plutôt votre synchronisation NTP.
Bogue n°4 : mauvaise commande de validation
L'ordre de validation correct est important. Si vous vérifiez la signature après avoir vérifié l'expiration, un attaquant peut créer un jeton avec un futur exp qui passe le contrôle temporel mais a une signature invalide.
Ordre de validation sécurisé :
- En-tête de décodage (algorithme de vérification)
- Vérifier la signature
- Vérifiez
exp(rejeter si expiré) - Vérifiez
nbf(rejeter si pas encore valide) - Cochez
iat(rejetez si trop vieux) - Vérifiez l'émetteur, l'audience et d'autres réclamations
La plupart des bibliothèques bien entretenues gèrent cela correctement, mais les middlewares personnalisés se trompent souvent.
Bug n°5 : confusion de fuseau horaire dans iat
Les horodatagesJWT sont toujours UTC. Mais les développeurs les créent parfois en utilisant l'heure locale :
// FAUX — fuseau horaire local
const iat = new Date('2026-03-27T14:00:00').getTime() / 1000; // CORRECT — UTC explicite
const iat = new Date('2026-03-27T14:00:00Z').getTime() / 1000;Sans le suffixe Z, JavaScript interprète la chaîne dans le fuseau horaire local, ce qui peut décaler l'horodatage d'heures.
Bogue n°6 : acceptation de jetons sans contrôle nbf
La revendication nbf est utile pour les jetons à activation retardée — par exemple, un jeton qui ne devrait fonctionner qu'après un déploiement planifié. Si votre validateur ignore nbf, ces jetons peuvent être utilisés avant l'heure d'activation prévue.
La plupart des bibliothèques valident nbf par défaut, mais vérifiez-le dans votre configuration, en particulier avec un middleware personnalisé.
Bogue n°7 : expiration trop longue
Définir exp sur 30 jours pour un jeton d'accès va à l'encontre de l'objectif des jetons de courte durée. Bonnes pratiques :
- Jetons d'accès : 5 à 15 minutes
- Actualiser les jetons : 7 à 30 jours (avec rotation)
- Jetons d'identification : 1 heure
Pour connaître les modèles de jetons d'actualisation sécurisés, consultez notre guide sur la rotation des jetons d'actualisation JWT.
Cas de test que chaque API devrait avoir
Ajoutez-les à votre suite de tests pour détecter rapidement les bogues de réclamation de temps :
exp dans le passé → 401exp exactement maintenant → 401 (limite)exp → 401nbf dans le futur → 401nbf légèrement dans le futur (dans les limites de tolérance) → 200iat dans le futur → 401 (indique une falsification)Paramètres par défaut sûrs pour les frameworks populaires
Node.js (jsonwebtoken)
jwt.sign(charge utile, secret, { expiresIn: '15m' });
jwt.verify(jeton, secret, { clockTolerance: 30, maxAge: '15m'
});Python (PyJWT)
jwt.decode(jeton, clé, algorithmes=['HS256'], marge de manœuvre=timedelta(secondes=30), options={'require': ['exp', 'iat']})Aller (golang-jwt)
parser := jwt.NewParser( jwt.WithLeeway(30 * time.Second), jwt.WithValidMethods([]string{"HS256"}),
)FAQ
Devrait-il être obligatoire ?
Bien que la spécification JWT indique que iat est facultatif, ce qui le rend obligatoire aide au débogage et aux pistes d'audit. Sans iat, vous ne pouvez pas déterminer quand un jeton a été créé, ce qui rend plus difficile la corrélation avec les événements de sécurité.
Quel décalage d'horloge dois-je autoriser ?
Une valeur par défaut de sécurité courante est de 30 à 60 secondes. Plus de 2 minutes présentent un risque de sécurité. Si vos systèmes ont besoin de plus, corrigez la synchronisation NTP au lieu d'élargir la tolérance d'asymétrie.
Quel code d'état HTTP dois-je renvoyer pour un jeton expiré ?
Retour 401 non autorisé avec un corps d'erreur clair comme {"error": "token_expired"}. Ne renvoyez pas 403 Forbidden — cela implique que le jeton est valide mais ne dispose pas d'autorisations, ce qui est une situation différente.
Outils et articles connexes
- JWT Decoder — inspectez les réclamations de temps dans n'importe quel jeton
- Erreurs de sécurité JWT — pièges JWT plus larges au-delà des réclamations temporelles
- Actualiser la rotation des jetons — modèle sécurisé pour les sessions de longue durée
- 2FA Guide — ajoutez une deuxième couche au-delà des jetons