Comment fonctionne un JWT
Un JSON Web Token (RFC 7519) est fait de trois morceaux de texte reliés par des points. L’en-tête indique comment le jeton est signé, le payload contient les claims, comme l’identité de l’utilisateur et la date d’expiration du jeton, et la signature prouve que les deux ont été produits par quelqu’un qui détient la clé. L’en-tête et le payload sont du JSON écrit en Base64url, une variante de Base64 sûre dans les URL.
header.payload.signature
header = base64url(UTF-8 JSON, e.g. {"alg":"HS256","typ":"JWT"})
payload = base64url(UTF-8 JSON with the claims)
signature = base64url(HMAC-SHA256(secret, header + "." + payload)) Base64url est un encodage, pas un chiffrement : quiconque a un jeton peut lire son payload, exactement comme le fait cette page, sans aucune clé. Ne mettez donc jamais de mots de passe ni d’autres secrets dans un JWT. Ce que la clé protège, c’est la signature : changez un seul caractère du payload et la signature ne correspond plus.
Avec HS256, HS384 et HS512, l’émetteur et le serveur partagent un secret et signent avec HMAC ; cette page peut les vérifier quand vous saisissez le secret. RS256, ES256 et les autres algorithmes à clé publique sont signés avec une clé privée et vérifiés avec la clé publique de l’émetteur, qu’un serveur obtient en général depuis une URL JWKS. Décoder un jeton n’est pas lui faire confiance : un serveur doit vérifier la signature, l’expiration et l’audience.
Exemple
Prenons l’exemple bien connu de jwt.io, eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c. La première partie donne l’en-tête {"alg":"HS256","typ":"JWT"} : une signature HMAC-SHA256. La deuxième donne le payload {"sub":"1234567890","name":"John Doe","iat":1516239022}. Son iat de 1516239022 signifie qu’il a été émis le 18 janvier 2018 à 01:30:22 UTC, et sans exp il n’expire jamais. La dernière partie est le HMAC-SHA256 de 32 octets des deux premières parties et du point qui les sépare ; avec le secret your-256-bit-secret il correspond, donc la signature est valide.
Les claims enregistrés
| Claim | Signification |
|---|---|
iss · Émetteur | Qui a créé et signé le jeton, en général l’URL du service de connexion. |
sub · Sujet | De qui parle le jeton, en général un identifiant d’utilisateur. |
aud · Audience | À qui le jeton est destiné. Un serveur doit rejeter un jeton dont l’audience n’est pas lui-même. |
exp · Expire le | Après cet instant, le jeton doit être rejeté. Secondes depuis le 1er janvier 1970 UTC. |
nbf · Pas avant | Avant cet instant, le jeton doit être rejeté. Secondes depuis le 1er janvier 1970 UTC. |
iat · Émis le | Quand le jeton a été créé. Secondes depuis le 1er janvier 1970 UTC. |
jti · ID du jeton | Un identifiant unique pour ce jeton, pour qu’un serveur puisse le bloquer ou remarquer qu’il est utilisé deux fois. |
Questions
Puis-je coller mon JWT ici sans risque ?
Oui. Le jeton est décodé par JavaScript dans votre navigateur, et la signature est vérifiée avec le WebCrypto intégré à votre navigateur. Ni le jeton ni le secret ne sont envoyés à un serveur ni conservés. Traitez tout de même un jeton actif comme un mot de passe : quiconque l’a peut s’en servir jusqu’à son expiration.
Peut-on décoder un JWT sans le secret ?
Oui. L’en-tête et le payload ne sont que du JSON encodé en Base64url : n’importe qui peut les lire sans clé. Le secret ou la clé publique ne sert qu’à vérifier que la signature est authentique. C’est pourquoi un JWT ne doit contenir ni mot de passe ni autre secret.
Comment vérifier si un JWT a expiré ?
Regardez le claim exp : c’est l’heure d’expiration en secondes depuis le 1er janvier 1970 UTC. Si l’heure actuelle l’a dépassée, le jeton a expiré. Cette page lit exp et nbf pour vous et montre le résultat sous forme de date et de texte comme « expiré il y a 3 jours » ou « expire dans 2 heures ».
Quelle est la différence entre HS256 et RS256 ?
HS256 signe avec HMAC et un secret partagé : qui peut vérifier un jeton peut aussi en fabriquer un. RS256 signe avec une clé privée RSA et se vérifie avec la clé publique : des services peuvent donc vérifier des jetons sans pouvoir en émettre. RS256 ou ES256 est le choix habituel quand plusieurs services vérifient les mêmes jetons.
Pourquoi la signature de mon JWT est-elle invalide ?
Le secret est faux, il est encodé en Base64 et demande l’option « Le secret est encodé en Base64 », ou le jeton a été modifié après sa signature. Copier un jeton peut aussi ajouter ou retirer des caractères. Si le jeton utilise RS256, ES256 ou un autre algorithme à clé publique, il ne peut pas du tout être vérifié avec un secret : il faut la clé publique de l’émetteur.