← Retour au blog Erreurs

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

Sécurité des développeurs27 mars 2026·8 minutes de lecture
JWT time claims debugging
Les réclamations de temps

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 :

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 :

  • Architectures de microservices avec horloges non synchronisées
  • Fonctions sans serveur où les conteneurs ont une dérive d'horloge
  • Applications mobiles où l'horloge de l'appareil est réglée manuellement
  • 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é :

    1. En-tête de décodage (algorithme de vérification)
    2. Vérifier la signature
    3. Vérifiez exp (rejeter si expiré)
    4. Vérifiez nbf (rejeter si pas encore valide)
    5. Cochez iat (rejetez si trop vieux)
    6. 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 horodatages

    JWT 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 :

    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 :

  • Token avec exp dans le passé → 401
  • Jeton avec exp exactement maintenant → 401 (limite)
  • Jeton sans exp → 401
  • Token avec nbf dans le futur → 401
  • Token avec nbf légèrement dans le futur (dans les limites de tolérance) → 200
  • Jeton avec iat dans le futur → 401 (indique une falsification)
  • Jeton avec horodatage en millisecondes → 401 (détecte le bug ms/s)
  • 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