Les JSON Web Tokens expliqués : revendications, signatures, validation sûre
Un JSON Web Token, ou JWT, est un court morceau de texte qui transporte quelques informations sur un utilisateur ou une requête — qui l’a émis, qui il concerne, quand il expire — accompagnées d’une signature qui permet au destinataire de vérifier que personne ne les a modifiées. Les API acceptent les JWT comme jetons d’accès, les systèmes de connexion les émettent comme jetons d’identité (ID tokens), et de nombreuses applications web s’en servent comme jetons de session. Ce guide explique ce qu’un JWT contient, en quoi les algorithmes de signature diffèrent, ce qu’un serveur doit vérifier avant de faire confiance à un jeton, les attaques qui exploitent les vérifications omises, et comment gérer l’expiration et le stockage.
Ce qu’est un JWT
Le format est défini par le RFC 7519, publié en 2015. Un JWT est un ensemble de revendications (claims) — des affirmations comme « le sujet est user-4821 » — encodées en JSON, puis signées ou chiffrées. Presque tous les JWT utilisés sont signés, sous la forme d’une JSON Web Signature (JWS, RFC 7515) : tout le monde peut les lire, mais, sans la clé, personne ne peut les modifier sans que cela se voie. Les JWT chiffrés (JWE, RFC 7516) masquent en plus leur contenu et sont beaucoup moins courants.
Deux normes définissent les JWT que vous avez le plus de chances de rencontrer. OpenID Connect, la couche de connexion utilisée par les boutons « Se connecter avec… », émet un jeton d’identité (ID token) qui doit être un JWT signé. Le profil JWT pour les jetons d’accès OAuth 2.0 (RFC 9068, 2021) définit la forme d’un jeton d’accès d’API lorsqu’il s’agit d’un JWT, même si les jetons d’accès de nombreux fournisseurs lui sont antérieurs et ne le suivent pas exactement.
Les trois parties
Un JWT signé se compose de trois morceaux de texte Base64URL reliés par des points : un en-tête, une charge utile (payload) et une signature. Voici un jeton complet, découpé au niveau des points :
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9En-tête : {"alg":"HS256","typ":"JWT"} — l’algorithme de signature et le type du jeton.eyJpc3MiOiJodHRwczovL2F1dGguZXhhbXBsZS5jb20iLCJzdWIiOiJ1c2VyLTQ4MjEiLCJhdWQiOiJodHRwczovL2FwaS5leGFtcGxlLmNvbSIsImlhdCI6MTc5MTk2ODQwMCwiZXhwIjoxNzkxOTY5MzAwLCJqdGkiOiJhOTFjMmU3ZiJ9Charge utile : émetteur https://auth.example.com, sujet user-4821, audience https://api.example.com, émis à 1791968400, expire à 1791969300, identifiant a91c2e7f.yQ_-tR1KNYT8HL6voHLYDvp5GY6EWmR33VMZ8BbD1usSignature : un HMAC-SHA256 des deux premières parties, calculé avec une clé secrète. Ce sont des données binaires, pas du JSON.
Base64URL est du Base64 ordinaire dont deux caractères sont remplacés pour que le résultat puisse figurer sans risque dans une URL (- et _ au lieu de + et /) ; dans les JWT, le remplissage = final est en outre supprimé. C’est un encodage, pas un chiffrement : n’importe qui peut retransformer les deux premières parties en JSON.
La signature est calculée sur le texte exact de l’en-tête et de la charge utile encodés, reliés par le point qui les sépare. Modifiez un seul caractère de l’un ou de l’autre — repoussez l’expiration, remplacez le sujet par quelqu’un d’autre — et la signature ne correspond plus.
Les revendications standard
Le RFC 7519 définit sept revendications enregistrées (registered claims). Toutes sont facultatives dans la norme de base ; les protocoles qui s’appuient sur elle en rendent certaines obligatoires.
| Revendication | Nom | Signification |
|---|---|---|
iss | Émetteur | Qui a créé et signé le jeton, généralement une URL |
sub | Sujet | Qui le jeton concerne, généralement un identifiant d’utilisateur |
aud | Audience | À qui le jeton est destiné ; un destinataire qui n’y figure pas doit le rejeter |
exp | Date d’expiration | À partir de ce moment, le jeton ne doit plus être accepté |
nbf | Pas avant | Avant ce moment, le jeton ne doit pas être accepté |
iat | Date d’émission | Quand le jeton a été créé, ce qui indique son âge |
jti | Identifiant du JWT | Un identifiant unique, utile contre le rejeu et pour la révocation |
Les trois revendications temporelles (exp, nbf et iat) sont des horodatages Unix : des nombres de secondes, normalement entiers, écoulées depuis le 1er janvier 1970 UTC. Dans l’exemple, 1791968400 correspond au 14 octobre 2026 à 9 h 00 UTC et 1791969300 à l’instant situé quinze minutes plus tard. Le RFC 7519 permet à un serveur de tolérer une petite marge pour des horloges qui ne concordent pas, « généralement pas plus de quelques minutes ».
Un jeton d’identité OpenID Connect doit contenir iss, sub, aud, exp et iat. Un jeton d’accès conforme au RFC 9068 doit contenir iss, exp, aud, sub, client_id, iat et jti, et porter le type at+jwt dans son en-tête. Tout le reste d’une charge utile — un nom, une adresse e-mail, des rôles — est défini par l’application.
Algorithmes de signature
La valeur alg de l’en-tête désigne l’algorithme. Ces algorithmes se répartissent en deux groupes : le secret partagé, où la même clé signe et vérifie, et la paire de clés, où une clé privée signe et une clé publique vérifie.
| alg | Méthode | Qui peut vérifier | Remarques |
|---|---|---|---|
HS256 | HMAC avec SHA-256 et un secret partagé | Quiconque détient le secret — et peut donc aussi créer des jetons | La clé doit faire au moins 256 bits (RFC 7518) et ne jamais être un mot de passe (RFC 8725). |
RS256 | Signature RSA avec SHA-256 | Quiconque détient la clé publique | Clés RSA d’au moins 2 048 bits. Le choix par défaut suggéré pour les jetons d’identité OpenID Connect. |
ES256 | ECDSA sur la courbe P-256 avec SHA-256 | Quiconque détient la clé publique | Clés et signatures beaucoup plus courtes qu’avec RSA. |
Ed25519 | EdDSA avec le jeu de paramètres Ed25519 | Quiconque détient la clé publique | Enregistré en octobre 2025 (RFC 9864), en remplacement de l’ancien identifiant EdDSA, moins précis, que de nombreuses bibliothèques utilisent encore. |
none | Aucune signature | Personne : le jeton n’est pas protégé | Les implémentations ne doivent pas l’accepter par défaut (RFC 7518). |
HS256 convient à un service unique qui émet et vérifie lui-même ses jetons. Lorsque d’autres services doivent vérifier les jetons — plusieurs API, ou des partenaires —, une paire de clés est plus adaptée : ils ne reçoivent que la clé publique, qui ne permet pas de créer des jetons.
Décoder n’est pas vérifier
N’importe qui peut écrire un JWT avec n’importe quelle charge utile ; le décoder, comme le fait le décodeur nTools, montre seulement ce qu’il affirme. Un jeton ne prouve quelque chose qu’une fois que le serveur qui le reçoit a effectué les vérifications suivantes, qui s’appuient sur le RFC 7519 et le RFC 8725, les bonnes pratiques actuelles de l’IETF pour les JWT :
- Fixez les algorithmes acceptés dans la configuration du serveur et rejetez tout le reste. Ne laissez jamais la valeur alg de l’en-tête du jeton décider de la manière dont il est vérifié, et n’utilisez chaque clé qu’avec un seul algorithme.
- Vérifiez la signature avec la clé qui appartient à l’émetteur attendu.
- Vérifiez que iss correspond à l’émetteur auquel vous faites confiance et que aud désigne votre service.
- Exigez la présence d’exp et des autres revendications dont dépend votre service, et comparez exp et nbf à l’heure actuelle avec une marge de quelques minutes au plus. Sinon, un jeton dépourvu d’exp n’expirerait jamais.
- Lorsqu’un type de jeton est défini, vérifiez typ — par exemple at+jwt pour les jetons d’accès — afin qu’un type de jeton ne puisse pas se faire passer pour un autre.
- Traitez les champs d’en-tête kid, jku et x5u comme des entrées non fiables : recherchez kid dans votre propre liste de clés, et ne prenez des clés que dans le jeu de clés configuré de l’émetteur ou à des adresses que vous avez autorisées à l’avance.
En pratique, ces vérifications relèvent d’une bibliothèque JWT maintenue, configurée avec l’émetteur, l’audience et les algorithmes attendus, et non d’un code écrit à la main.
Les attaques classiques
La plupart des vulnérabilités des JWT ne sont pas des faiblesses de la cryptographie, mais des vérifications qu’un serveur a omises. Le RFC 8725 décrit les suivantes :
- alg none : l’attaquant remplace l’algorithme de l’en-tête par none et supprime la signature. Un serveur qui fait confiance à l’en-tête accepte un jeton que n’importe qui aurait pu écrire.
- Confusion de clés : un serveur attend RS256, mais l’attaquant envoie un jeton marqué HS256, signé avec la propre clé publique du serveur comme s’il s’agissait d’un secret HMAC. Une bibliothèque qui choisit l’algorithme d’après l’en-tête vérifie alors le jeton avec cette même clé publique — et la vérification réussit.
- Secrets faibles : un secret HS256 court, ou qui est un mot de passe, peut être deviné hors ligne, à partir d’un seul jeton intercepté, sans aucun contact avec le serveur. Muni du secret, l’attaquant peut signer n’importe quel jeton.
- Substitution : un jeton valide émis pour un service, ou pour un usage, est présenté à un autre — un jeton d’identité utilisé comme jeton d’accès, ou un jeton destiné à une API utilisé auprès d’une autre.
Les vérifications de la section précédente parent chacune d’elles : une liste fixe d’algorithmes, avec chaque clé liée à un seul algorithme, arrête les deux premières ; un secret aléatoire d’au moins 256 bits, la troisième ; et la vérification d’aud et de typ, la dernière.
La charge utile est publique
Une signature protège contre les modifications, pas contre la lecture. Les jetons circulent dans les en-têtes des requêtes (et parfois, imprudemment, dans les URL) et finissent dans les journaux, le stockage du navigateur et les en-têtes Referer ; quiconque en voit un peut le décoder. Les mots de passe, les secrets et les données personnelles dont le destinataire n’a pas besoin n’ont pas leur place dans une charge utile.
Lorsque des revendications doivent accompagner le jeton tout en restant confidentielles, le jeton peut être chiffré sous forme de JWE, qui compte cinq parties au lieu de trois. L’alternative suggérée par l’OWASP est souvent plus simple : conserver les données sur le serveur et remettre au client un jeton de référence opaque, qui ne signifie rien en lui-même.
Expiration, révocation et stockage
Un JWT signé est valide jusqu’à exp, où qu’il se trouve : le serveur n’a pas besoin de le rechercher, ce qui rend les JWT rapides à vérifier, mais explique aussi pourquoi un jeton volé continue de fonctionner. Des durées d’expiration courtes limitent les dégâts. Pour retirer un jeton plus tôt, le serveur doit malgré tout conserver un état — l’OWASP décrit une liste de refus (denylist) indexée sur les revendications jti et iss ; une entrée peut être supprimée une fois la date exp du jeton dépassée.
Les applications qui ont besoin de sessions plus longues associent un jeton d’accès de courte durée à un jeton d’actualisation (refresh token) servant à en obtenir de nouveaux. Les bonnes pratiques de sécurité d’OAuth 2.0 (RFC 9700, janvier 2025) exigent que les jetons d’actualisation délivrés aux clients publics — généralement les applications de navigateur et mobiles, qui ne peuvent pas garder un secret — soient soit liés à l’expéditeur (sender-constrained), c’est-à-dire rattachés cryptographiquement à ce client, soit renouvelés par rotation : remplacés par un nouveau à chaque utilisation, l’ancien étant invalidé.
Dans le navigateur, l’OWASP déconseille de conserver les identifiants de session dans localStorage, car une seule faille de cross-site scripting suffit à lire tout ce qui y est stocké. Un cookie marqué HttpOnly ne peut pas du tout être lu par JavaScript, mais il doit aussi être Secure et SameSite, et un script injecté dans la page peut toujours envoyer des requêtes qui le transportent.
Questions fréquentes
Un JWT est-il chiffré ?
En général, non. La plupart des JWT sont signés, et leur en-tête et leur charge utile sont seulement encodés en Base64URL : quiconque possède le jeton peut donc les lire. Seul un JWE, à cinq parties, masque son contenu.
Peut-on lire un JWT sans la clé ?
Oui, sauf s’il s’agit d’un JWE chiffré. Pour un JWT signé, la clé sert à créer ou à vérifier la signature, pas à lire l’en-tête et la charge utile.
Combien de temps un JWT doit-il rester valide ?
Les normes ne fixent aucune durée unique. Le conseil habituel : une durée aussi courte que l’application le permet, avec des jetons d’actualisation pour les sessions plus longues ; un jeton perdu n’est alors utilisable que pendant peu de temps.
Faut-il utiliser des JWT pour les sessions d’un site web ?
Pas forcément. Un identifiant de session classique dans un cookie peut être révoqué immédiatement sur le serveur ; un JWT évite une consultation à chaque requête, mais ne peut pas être retiré avant son expiration sans conserver un état supplémentaire.
Est-il sûr de coller un jeton dans le décodeur nTools ?
Le jeton est décodé dans votre navigateur et n’est envoyé nulle part. Préférez malgré tout un jeton expiré ou de test : un jeton encore valide est un identifiant de connexion.
Partie précédente ← Le hachage expliqué : SHA-256, sommes de contrôle et mots de passe
Références
- RFC 7519 — JSON Web Token (JWT) (May 2015)
- RFC 7515 — JSON Web Signature (JWS) (May 2015)
- RFC 7516 — JSON Web Encryption (JWE) (May 2015)
- RFC 7518 — JSON Web Algorithms (JWA) (May 2015)
- RFC 8725 — JSON Web Token Best Current Practices (February 2020)
- RFC 9068 — JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens (October 2021)
- RFC 9700 — Best Current Practice for OAuth 2.0 Security (January 2025)
- RFC 9864 — Fully-Specified Algorithms for JOSE and COSE (October 2025)
- OpenID Connect Core 1.0 incorporating errata set 2 (December 2023)
- OWASP — JSON Web Token Cheat Sheet
- OWASP — HTML5 Security Cheat Sheet: Local Storage
Guides associés
- Les adresses IP expliquées : IPv4, IPv6 et les plages réservées
- Les sous-réseaux et le CIDR expliqués : préfixes, masques et hôtes utilisables
- Les enregistrements DNS expliqués : A, AAAA, CNAME, MX, TXT et NS
- Un site est-il en panne, ou est-ce chez vous ? Comment vérifier, étape par étape
- Codes de statut HTTP et erreurs Cloudflare : ce que signifient 403, 404, 429, 500, 503 et 521
- Les timestamps Unix et les fuseaux horaires expliqués
- Compter les dates : jours entre deux dates, années bissextiles et semaines ISO
- Les formats d’image expliqués : JPEG, PNG, WebP, AVIF et HEIC
- La robustesse des mots de passe expliquée : longueur, entropie et phrases de passe
- Le hachage expliqué : SHA-256, sommes de contrôle et mots de passe
- Les UUID expliqués : v4, v7 et comment choisir la bonne version
- Les QR codes expliqués : capacité, correction d’erreurs et taille d’impression
- Les pourcentages et la TVA expliqués : ajouter, retirer et cumuler
- Erreurs JSON courantes et comment les corriger
- Modifier des PDF dans le navigateur : ce que la fusion, la division et la conversion conservent