Français

Ce que prouve un JWT décodé

Un décodeur JWT vous montre ce que prétend un jeton. Il ne peut pas vous montrer si ces affirmations sont vraies. Ce guide explique ce que sont les trois segments, ce que le décodage établit et n'établit pas, ainsi que les attaques qui se situent dans l'écart entre les deux.

Trois segments, dont deux uniquement en JSON

Un JWT dans sa forme courante est un JWS : trois segments base64url séparés par des points. Le premier est un en-tête, le second une charge utile, le troisième une signature.

L'en-tête et la charge utile sont des objets JSON ordinaires qui ont été codés en base64url. Codé, pas crypté. Toute personne détenant le jeton peut lire les deux instantanément, sans clé – ce n’est pas un défaut, c’est la conception. Un JWT est une déclaration signée, pas une enveloppe scellée. La signature garantit que la déclaration n'a pas été modifiée ; cela ne fait rien pour le garder privé.

La conséquence mérite d’être clairement indiquée car elle est régulièrement manquée : ne mettez jamais quoi que ce soit de confidentiel dans une charge utile JWT. Pas un mot de passe, pas un identifiant national complet, pas des détails internes du système. Supposons que la charge utile soit publique, car pour quiconque possède le jeton, elle l'est.

Le troisième segment est la signature, calculée sur les deux premiers. C'est la seule partie qui comporte une valeur de sécurité, et c'est la partie qu'un décodeur ne peut pas évaluer.

Ce que prouve le décodage : rien

C’est le point de tout le guide. Le décodage d'un JWT analyse deux chaînes base64url en JSON. Cela confirme que le jeton est bien formé. Cela ne confirme pas que le jeton est authentique, qu'il a été émis par la partie nommée dans la revendication "iss", que les revendications n'ont pas été modifiées ou qu'il a jamais été valide.

N'importe qui peut construire un jeton. Prenez n'importe quel JWT, remplacez "role": "user" par "role": "admin", ré-encodez la charge utile, agrafez n'importe quelle signature à la fin, et un décodeur affichera vos revendications modifiées avec autant de confiance qu'il a affiché l'original. Il n'a aucun moyen de connaître la différence, car vérifier la différence est une opération différente nécessitant une clé que le décodeur ne possède pas.

Ainsi, lorsqu'un décodeur - celui-ci ou tout autre - vous montre "exp: 2026-01-01", ce qu'il vous dit en réalité, c'est : ce jeton contient une affirmation selon laquelle il expire à cette date. La signification de cette affirmation dépend entièrement de la validité de la signature, ce qui n’a pas été vérifié.

Cet outil décode uniquement, et l'indique à chaque fois sur la page, à côté des résultats. Pas dans une note de bas de page. La raison en est qu'un décodeur qui reste discret sur ce sujet entraîne ses utilisateurs à lire des données non vérifiées comme si elles l'étaient, et cette habitude est à l'origine de toute une famille de bugs d'authentification.

Pourquoi cet outil n'offre pas de vérification

La vérification nécessite trois éléments qu'une page Web ne peut pas avoir de manière responsable : la clé de l'émetteur, l'algorithme épinglé à l'avance et une politique sur ce qu'il faut rejeter.

La clé est le problème évident. Pour les algorithmes HMAC (HS256 et amis), la clé est un secret partagé – le même secret utilisé pour créer des jetons. Le coller dans une page Web signifie coller un identifiant capable de créer des jetons valides dans une page Web. Pour RSA et ECDSA, la clé publique n'est pas secrète, mais vous devrez quand même récupérer la bonne clé à partir du bon point de terminaison JWKS et avoir la confiance que vous aviez.

L’algorithme est le problème subtil et la source de deux attaques bien connues. Le premier est alg : "none" : l'en-tête prétend que le jeton n'est pas signé, et un vérificateur qui honore l'en-tête plutôt que sa propre configuration accepte n'importe quoi. La seconde est la confusion entre RS256 et HS256 : l’attaquant prend une clé publique – qui est, par définition, publique – modifie l’en-tête pour indiquer HS256 et signe le jeton en utilisant cette clé publique comme secret HMAC. Un vérificateur qui lit l'algorithme à partir du jeton et recherche « la clé » le validera.

Les deux attaques proviennent de la même erreur : laisser le jeton indiquer au vérificateur comment vérifier le jeton. Un vérificateur correct ignore l’algorithme de l’en-tête et utilise celui avec lequel il a été configuré. C'est une décision qui appartient au système qui fait confiance au jeton – et non à un outil pratique, ni à celui qui a collé quelque chose dans un formulaire.

Pourquoi ne pas coller les jetons de production n'importe où

Un jeton d'accès est un identifiant de porteur. C'est ce que signifie "bearer" dans l'en-tête Autorisation : celui qui le porte, c'est vous. Il n’y a pas de deuxième facteur et généralement aucun moyen de distinguer un jeton volé d’un jeton légitime. Jusqu'à son expiration, il s'agit d'une clé fonctionnelle pour votre compte.

Ainsi, coller un jeton actif dans n'importe quelle page Web revient à transmettre un identifiant à cette page. Celui-ci décode tout localement et ne fait aucune requête réseau après le chargement de la page – vous pouvez le confirmer dans le panneau réseau de votre navigateur, et vous devriez le faire, car cela prend dix secondes. Mais remarquez ce qu’est réellement cet argument : une affirmation, sur un site Web, que le site Web est digne de confiance. Chaque site qui exfiltre des jetons fait exactement la même affirmation, et un visiteur ne peut pas faire la différence en un coup d'œil.

L'habitude sûre ne dépend pas du jugement correct des sites. Utilisez des jetons expirés, des jetons d'environnement de test ou des jetons que vous avez créés à cet effet. Si vous avez déjà collé un jeton de production quelque part, n'importe où, faites-le pivoter. La révocation est bon marché ; un incident ne l’est pas.

La même chose s'applique avec plus de force à la signature des clés. Il n'y a aucune raison légitime de saisir un secret HMAC ou une clé privée dans une page Web, et tout site en demandant un pour "verify" votre jeton demande la possibilité de falsifier des jetons. C'est la raison concrète pour laquelle cet outil n'a pas de fonction de vérification : la fonctionnalité nécessite la demande.

Lire les affirmations qui comptent

RFC 7519 enregistre un petit ensemble de noms de revendications. "iss" est l'émetteur, "sub" le sujet sur lequel porte le jeton, "aud" le public visé, "exp" l'expiration, "nbf" la première heure valide, "iat" l'heure d'émission et "jti" un identifiant unique pour la détection de relecture. Tout le reste est spécifique à l'application.

Les revendications de temps sont des valeurs NumericDate : secondes depuis l'époque Unix, pas millisecondes. Cela fait constamment trébucher les gens, car la plupart des valeurs de temps JavaScript sont des millisecondes. Un jeton qui semble expirer dans 1970 reçoit généralement une valeur en millisecondes ; celui qui semble expirer dans l'année 55000 a généralement une deuxième valeur multipliée par 1000 quelque part.

"aud" mérite une attention particulière lors du débogage. Un jeton parfaitement valide peut toujours être un mauvais jeton, car il a été émis pour un public différent. Un vérificateur qui vérifie la signature mais pas l’audience acceptera entièrement un jeton créé pour un autre service – ce qui constitue une véritable voie d’élévation de privilèges dans les systèmes partageant un fournisseur d’identité.

Cet outil affiche les réclamations de temps au format UTC, marque un jeton expiré comme expiré et l'associe à un rappel que la réclamation d'expiration ne signifie quelque chose que si la signature est valide. Le rappel est là parce que « il dit qu'il n'a pas expiré » est le moment exact où l'habitude des données non vérifiées fait des dégâts.

Une courte liste de contrôle pour le système qui fait confiance

Si vous écrivez le code qui accepte les jetons plutôt que de simplement en inspecter un, voici la version courte de ce que fait un vérificateur correct.

  1. Vérifiez d'abord la signature, avec une clé que vous avez obtenue hors bande, avant de lire toute réclamation.
  2. Épinglez l'algorithme dans votre propre configuration. Ne le lisez jamais à partir de l’en-tête du jeton. Rejetez "none" sans condition.
  3. Vérifiez "exp" et "nbf" par rapport à une horloge fiable, avec au plus une petite tolérance pour le biais.
  4. Vérifiez "iss" et "aud" par rapport aux valeurs attendues. Une signature valide sur un jeton destiné à quelqu'un d'autre n'est toujours pas le bon jeton.
  5. Utilisez une bibliothèque approuvée pour votre plate-forme plutôt que de l'assembler vous-même. Chaque élément de cette liste y figure parce que les implémentations se sont trompées.
  6. Gardez la durée de vie des jetons courte et disposez d’un chemin de révocation. Les jetons de courte durée limitent les dégâts de la fuite que vous n'avez pas encore remarqué.

Qu'arrive-t-il à ce que vous collez

  • Chaque conversion, hachage, décodage et différence s'exécute dans l'onglet de votre navigateur. Aucune entrée n'est téléchargée, enregistrée ou stockée sur un serveur, car aucun serveur n'est impliqué une fois la page chargée.
  • Les hachages proviennent de la propre implémentation Web Crypto du navigateur et les UUID de son générateur aléatoire cryptographiquement sécurisé. Ni l’un ni l’autre n’implique un appel réseau.
  • Rien de ce que vous tapez n’est écrit dans le stockage local ou dans un cookie. Le rechargement de la page la supprime ; la fermeture de l'onglet le supprime.
  • Les analyses à l'échelle du site s'exécutent uniquement sur l'hôte de production canonique configuré et sont divulguées dans la politique de confidentialité ; les hôtes locaux et de prévisualisation le refusent. Les valeurs collées, les jetons, les URL et le contenu des fichiers sont exclus des propres événements d'analyse de ToolAcre. La publicité est désactivée dans la configuration actuelle.
  • Cela dit : un JWT ou une clé API est un identifiant en direct. La bonne habitude est de ne jamais en coller un dans une page Web que vous n’avez pas écrite, aussi dignes de confiance que ses affirmations – y compris celle-ci.

Questions

Cet outil vérifie-t-il la signature ?

Non, et cela ne le sera jamais. Il décode l'en-tête et la charge utile et vous montre ce qu'ils contiennent. Il ne vérifie pas la signature, donc rien de ce qu'il affiche ne prouve que le jeton est authentique, inchangé ou émis par la personne qu'il nomme.

Alors, comment puis-je savoir si un jeton est authentique ?

En vérifiant la signature avec la clé de l'émetteur, à l'aide d'une bibliothèque vérifiée, avec l'algorithme épinglé dans votre propre configuration plutôt que lu à partir du jeton. C'est un travail pour le service qui fait confiance au jeton, dans un environnement qui détient légitimement la clé.

Mon token est-il envoyé quelque part lorsque je le décode ici ?

Non. Le décodage s'effectue dans l'onglet de votre navigateur à l'aide du JavaScript de la page, et la page n'effectue aucune requête réseau après son chargement. Vous pouvez le vérifier dans le panneau réseau de votre navigateur. Vous ne devriez toujours pas coller des jetons de production dans des outils Web par habitude, car cette habitude doit fonctionner sur des sites qui ne sont pas honnêtes à ce sujet.

Pourquoi n'importe qui peut-il lire ma charge utile JWT ?

Parce que la charge utile est codée en base64url et non chiffrée. Un JWS est une déclaration signée et non scellée. Si vous avez besoin que le contenu soit illisible, vous avez besoin de JWE, le format de jeton crypté – et un décodeur ne peut alors rien vous montrer sans la clé.

Qu'est-ce que alg : "none" ?

Une valeur d'en-tête déclarant que le jeton n'est pas signé. Il existe dans la spécification pour les contextes où l'intégrité est garantie par d'autres moyens, et c'est un piège permanent : un vérificateur qui fait confiance à l'algorithme de l'en-tête acceptera tout jeton qui revendique "none". Cet outil le signale chaque fois qu'il apparaît.

Mon token comporte cinq segments et ne sera pas décodé. Pourquoi?

Cinq segments signifient un JWE – un jeton crypté – plutôt qu'un JWS signé. Son contenu ne peut pas être lu sans la clé de déchiffrement, il n’y a donc vraiment rien à afficher pour un décodeur. Cet outil identifie ce cas explicitement au lieu de signaler un vague échec d'analyse.

L'expiration semble erronée d'un facteur 1000.

Les revendications de temps JWT sont NumericDate : secondes depuis l'époque, et non millisecondes. Une valeur produite par Date.now() est mille fois trop grande. L'utilitaire d'horodatage de cette boîte à outils convertit entre les deux et vous indique toujours quelle unité il a utilisée.

Est-il sûr de stocker un JWT dans localStorage ?

C'est un compromis, pas un oui ou un non. localStorage est lisible par n'importe quel JavaScript exécuté sur votre origine, donc une seule vulnérabilité XSS exfiltre le jeton. Un cookie httpOnly n'est pas lisible par JavaScript mais nécessite une protection CSRF. Le résumé honnête est que ni l’un ni l’autre n’est gratuit et que la décision appartient au modèle de menace de votre application.

Limites

  • Cet outil décode uniquement. Il ne vérifie pas les signatures, et il s'agit d'une décision de conception permanente plutôt que d'une fonctionnalité manquante – voir le guide ci-dessus pour savoir pourquoi.
  • Les jetons cryptés (JWE, cinq segments) ne peuvent pas être décodés sans la clé. L'outil les identifie et s'arrête.
  • Les JWT imbriqués – un jeton dont la charge utile est elle-même un jeton – ne sont pas déballés automatiquement. Décodez le jeton interne dans le cadre d’une étape distincte.
  • Les significations des revendications au-delà de l'ensemble enregistré défini dans la RFC 7519 sont spécifiques à l'application, de sorte que l'outil affiche leurs valeurs sans les interpréter.
  • Une expiration indiquée ici reflète uniquement ce que le jeton prétend sur lui-même. Le sens de cette affirmation dépend d'une signature que cet outil ne vérifie pas.
  • Les jetons supérieurs à 200,000 caractères sont refusés. Tout JWT réel est d'un ordre de grandeur plus petit.