Sur cette page

Pourquoi vous ne devriez pas coller vos JWT dans jwt.io (et quoi utiliser à la place)

Un jeton de production atterrit dans votre terminal. Vous devez savoir qui l’a émis, quand il expire et quelles revendications il porte — rapidement. Alors vous faites ce que la plupart des développeurs font : vous ouvrez jwt.io, vous collez le jeton, et vous lisez la charge utile décodée.

Stop. Ce JWT est un identifiant. Le site dans lequel vous venez de le coller est exploité par un tiers, avec un historique d’analytics ajoutés discrètement et une réécriture en cours déclenchée par les préoccupations de confidentialité de ses propres mainteneurs. Il existe une meilleure réponse, et la requête de recherche pour laquelle vous êtes probablement ici — « alternative jwt.io confidentialité » — en a une véritable.

Ce guide passe en revue le modèle de menace réel, ce que l’équipe de jwt.io a reconnu publiquement, et comment vérifier qu’un débogueur JWT n’envoie jamais votre jeton nulle part. Si vous préférez sauter l’explication et décoder un jeton tout de suite dans votre navigateur, le JWT Decoder est entièrement côté client — ouvrez-le, déconnectez-vous d’internet, et regardez-le fonctionner quand même.

TL;DR : jwt.io est-il sûr ?

Réponse courte : la page d’accueil de jwt.io vous avertit elle-même. Son propre texte d’avertissement dit, en substance : « Les JWT sont des identifiants — faites attention à l’endroit où vous les collez. » Cet avertissement est correct, et il s’applique à jwt.io autant qu’à tout autre outil en ligne.

Pour des jetons de démonstration non sensibles, jwt.io fait l’affaire. Pour tout ce qui provient d’un environnement réel — production, préproduction, sandbox, intégration partenaire — supposez le réglage par défaut le plus sûr : décodez localement. Le reste de cet article explique le pourquoi et le comment.

Ce qui se passe réellement quand vous collez un JWT dans un décodeur en ligne

Un JWT (JSON Web Token) est composé de trois segments encodés en Base64URL et joints par des points : un en-tête, une charge utile et une signature. L’en-tête et la charge utile ne sont pas chiffrés ; ils sont simplement encodés. Quiconque détient le jeton peut en lire le contenu — y compris tout service qui le reçoit, même si c’est seulement pour le « décoder côté client ».

Plusieurs choses peuvent se produire entre le moment où vous appuyez sur Cmd-V et celui où vous voyez les revendications décodées :

  • Historique du navigateur. De nombreux outils ont historiquement transmis le jeton dans la chaîne de requête de l’URL, ce qui signifie qu’il se retrouve dans l’historique du navigateur, dans les en-têtes Referer des onglets parents, et dans tout backend d’historique synchronisé (Chrome Sync, Firefox Sync, profils Edge).
  • Journaux serveur. Même les pages qui décodent en JavaScript émettent toujours des requêtes pour les analytics, les polices et les scripts publicitaires. Si le jeton est dans l’URL, il apparaît dans les en-têtes Referer envoyés à ces tiers.
  • SDK de télémétrie. Jusqu’en septembre 2019, jwt.io envoyait des métriques depuis la page malgré son affichage d’un fonctionnement uniquement côté client, comme l’a documenté l’ingénieur Jamie Tanna (Capital One Open Banking). « Côté client » est une affirmation, pas un audit.
  • Modifications futures du code. Un site qui est côté client aujourd’hui peut publier une modification côté serveur demain. Comme l’a formulé un commentateur largement cité sur Hacker News : « Si vous prenez l’habitude d’utiliser ces outils, l’un d’entre eux finira par être compromis. Que ce soit par un piratage technique, une pression financière, un rachat par une entité immorale, ou un employé mécontent quelque part sur le chemin. »

Rien de tout cela ne nécessite de malveillance. Il suffit que quelqu’un, quelque part, entre votre clavier et vos yeux, possède une copie de votre jeton suffisamment longtemps pour en abuser.

Le véritable modèle de menace

La première objection est toujours la même : « Mais le jeton expire dans 15 minutes — quel est le pire qui puisse arriver ? » Plusieurs choses, en fait.

Rejeu dans la fenêtre d’expiration. Quinze minutes, c’est largement assez de temps pour qu’un script surveillant un pipeline d’analytics, un fichier journal divulgué ou un en-tête referrer attrape le jeton et appelle votre API en se faisant passer pour vous. Si le jeton est un jeton de rafraîchissement (qui peut être valide pendant des jours ou des semaines), la fenêtre est bien plus large.

Les revendications sont une carte au trésor. Une charge utile typique inclut l’identifiant utilisateur (sub), l’identifiant de tenant, la liste des rôles, l’audience (aud), l’émetteur (iss), et souvent des revendications personnalisées comme l’e-mail, les drapeaux internes ou les droits aux fonctionnalités. Même un JWT expiré indique à un attaquant exactement quel utilisateur cibler, quelle API frapper, et à quoi ressemble une élévation de privilèges dans votre entreprise.

Les jetons hors production divulguent des secrets de forme production. Les JWT de sandbox Open Banking et les jetons d’intégration partenaire contiennent souvent des empreintes de certificat, des identifiants de clé et des URL d’audience qui correspondent directement à l’infrastructure de production. Jamie Tanna a décrit avoir été « brûlé à plusieurs reprises » par des collègues collant des JWT hors production et des certificats de sandbox Open Banking dans jwt.io.

Les retombées de conformité sont indépendantes de l’impact. Si votre entreprise est soumise à SOC 2, ISO 27001, PCI-DSS, ou à toute règle apparentée à HIPAA, coller un identifiant dans un service tiers est déclarable indépendamment du fait qu’une exploitation ait réellement eu lieu. La constatation d’audit est l’incident.

Les bugs de manipulation des JWT continuent d’être livrés. CVE-2026-29000, un contournement d’authentification dans le JwtAuthenticator de pac4j, a été divulgué plus tôt cette année. Les bugs de validation de jetons ne sont pas un problème de 2018 ; le débogage des JWT est exactement le moment où les développeurs se tournent vers les outils en ligne, et exactement le moment où les jetons divulgués sont les plus utiles à un attaquant qui surveille la même bibliothèque.

Ce que les mainteneurs de jwt.io ont eux-mêmes déclaré

L’argument le plus fort contre le collage de jetons dans jwt.io vient de jwt.io lui-même.

Dans l’issue GitHub n°700, « The Future of JWT.io », ouverte le 19 juillet 2024, un ingénieur DevRel d’Okta/Auth0 a écrit — textuellement — que « le passage d’un jeton au site via les paramètres de requête de l’URL a créé des préoccupations concernant la confidentialité du jeton ». L’issue expose une réécriture prévue qui passe des paramètres de requête d’URL aux fragments d’URL, sépare décodage/encodage/vérification en widgets distincts, et cesse de charger automatiquement les jetons depuis l’URL au chargement de la page.

C’est l’équipe qui exploite jwt.io qui s’engage publiquement à une refonte parce que la gestion existante n’est pas assez privée. Au moment de la rédaction de cet article, certaines parties de la refonte ont été livrées et d’autres non. Le statut exact n’a aucune importance pour vous : le point essentiel est que la barre de confidentialité de l’opérateur lui-même est plus haute que celle du site historique, et vous n’avez aucun moyen fiable de savoir quelle version un visiteur donné utilise un jour donné.

Par ailleurs, l’article The State of JWT Libraries on JWT.io de PentesterLab (28 mars 2025) a constaté que la liste de bibliothèques sélectionnées par jwt.io recommande toujours des dépôts archivés en 2018, des bibliothèques mises à jour pour la dernière fois en 2015, et au moins une implémentation qui prend en charge HMAC-MD5 (un algorithme qui n’existe pas dans la spécification JWT). Une bibliothèque de la liste extrait la valeur alg via une regex au lieu d’analyser le JSON ; une autre signe avec « une chaîne aléatoire de longueur aléatoire et la charge utile comme secret ». Louis Nyffenegger, fondateur de PentesterLab, résume : « Les développeurs peuvent, sans le savoir, choisir des bibliothèques peu sûres ou abandonnées. »

Le catalogue de bibliothèques n’est pas le même problème que la confidentialité des jetons, mais il pointe dans la même direction : la propriété « populaire et pratique » fait beaucoup de travail injustifié dans votre modèle de confiance.

Ce que signifie réellement « décodeur JWT côté client »

Un décodeur véritablement côté client fait trois choses :

  1. Charge le code via HTTPS une seule fois, puis s’exécute entièrement dans l’onglet de votre navigateur.
  2. Effectue le décodage en JavaScript en utilisant atob() (ou un assistant Base64URL) et JSON.parse() sur les segments d’en-tête et de charge utile.
  3. Vérifie éventuellement les signatures en utilisant l’API Web Crypto, la même primitive auditée qui sécurise vos négociations TLS.

Pas de fetch, pas de XHR, pas de navigator.sendBeacon, pas de ping d’analytics emportant le jeton. Les octets du jeton ne quittent jamais la mémoire de l’onglet.

Ce n’est pas le comportement par défaut. C’est une propriété qu’un outil possède ou ne possède pas, et que vous pouvez vérifier vous-même en moins d’une minute.

Comment vérifier qu’un outil JWT est réellement côté client (recette en 60 secondes)

C’est la compétence la plus utile de cet article. Exécutez-la sur tout outil qui touche à des identifiants, y compris ceux de remove.sh.

Méthode 1 : le test du mode avion

  1. Ouvrez l’outil dans un nouvel onglet.
  2. Attendez que la page soit complètement chargée.
  3. Désactivez le Wi-Fi ou activez le mode avion.
  4. Collez un jeton et décodez.

Si l’en-tête et la charge utile décodés apparaissent, l’outil effectue le travail localement. Si vous voyez un spinner ou une erreur, l’outil effectue un appel serveur — probablement avec votre jeton dans le corps de la requête. C’est la vérification la plus rapide et la plus définitive.

Méthode 2 : l’audit réseau via DevTools

  1. Ouvrez les DevTools (F12 ou Cmd+Option+I).
  2. Passez à l’onglet Réseau.
  3. Filtrez par Fetch/XHR.
  4. Cliquez sur Effacer (l’icône d’interdiction), puis collez votre jeton.

Un décodeur côté client n’affichera aucune requête Fetch/XHR pendant le décodage. Si vous voyez une requête, cliquez dessus et inspectez l’onglet Charge utile — votre jeton est peut-être dans le corps de la requête ou dans la chaîne de requête. Même s’il n’y est pas, l’outil contacte le réseau à chaque frappe, ce qui suffit à le disqualifier pour un travail sensible.

Méthode 3 : lisez la barre d’adresse

Si l’outil place le jeton directement dans l’URL — ?token=eyJhbG... — ce jeton se trouve désormais dans l’historique de votre navigateur, dans l’historique de votre shell si vous avez copié l’URL, dans l’en-tête Referer envoyé à chaque script d’analytics de la page suivante que vous visitez, et dans tout gestionnaire de presse-papiers que vous utilisez. Les outils qui utilisent les fragments d’URL (#token=...) sont meilleurs, car les fragments ne sont pas envoyés aux serveurs, mais les paramètres de requête sont un disqualifiant immédiat.

Essayez vous-même : Ouvrez le JWT Decoder sur remove.sh, puis exécutez les trois vérifications. Le panneau Réseau reste vide après le chargement de la page, l’URL ne contient jamais votre jeton, et la page fonctionne en avion. C’est une propriété vérifiable, pas une affirmation marketing.

Une alternative à jwt.io axée sur la confidentialité : décoder dans votre navigateur

Si vous nous avez suivis jusqu’ici, la recommandation n’est pas surprenante : utilisez un décodeur que vous pouvez vérifier, puis vérifiez-le.

Le JWT Decoder de remove.sh est construit sur les mêmes primitives décrites dans cet article. Il utilise atob() pour les segments Base64URL, JSON.parse() pour l’en-tête et la charge utile, et l’API Web Crypto pour la vérification de signature HMAC (HS256/HS384/HS512). Les jetons ne sont jamais sérialisés dans l’URL. Il n’y a pas de SDK d’analytics qui lit le champ d’entrée. L’outil rapporte le statut d’expiration en comparant la revendication exp à votre horloge locale, et étiquette les revendications enregistrées (iss, sub, aud, iat, nbf, jti) afin que vous n’ayez pas à les mémoriser.

Ce qu’il ne fait pas, à ce jour, c’est vérifier les signatures RSA ou ECDSA (RS256/ES256). Pour les signatures asymétriques, vous avez deux options raisonnables, toutes deux couvertes dans la section suivante.

Si votre jeton est enveloppé dans Authorization: Bearer eyJhbG..., collez le tout — le décodeur retire automatiquement le préfixe Bearer . Si vous n’avez que les segments encodés en Base64 et que vous voulez les inspecter bruts, le Base64 Encoder & Decoder décode les segments individuels sans essayer de les interpréter comme un JWT, ce qui est utile lorsque vous suspectez un jeton mal formé.

Vérifier les signatures sans exposer votre secret

Le décodage vous indique ce qu’un jeton revendique. La vérification vous indique s’il faut lui faire confiance. Les deux opérations ne sont pas identiques, et leurs enjeux en matière de confidentialité sont différents.

HMAC (HS256/HS384/HS512)

La vérification HMAC nécessite le secret partagé. C’est l’enjeu dangereux : un secret divulgué signifie que n’importe qui peut générer des jetons valides pour votre service. Ne collez jamais un secret HMAC dans un outil en ligne dont vous ne pouvez pas vérifier qu’il est local.

Le JWT Decoder de remove.sh vérifie les signatures HMAC avec l’API Web Crypto. Le secret reste dans la mémoire de votre onglet et n’est jamais sérialisé sur le réseau. Vous pouvez le confirmer en exécutant le test du mode avion avec à la fois le jeton et le secret remplis — la vérification s’effectue toujours.

Si vous voulez une garantie plus forte que même un onglet de navigateur audité, descendez au niveau de la recette openssl locale ci-dessous pour recalculer le HMAC à partir de la portion en-tête-charge utile et le comparer vous-même au segment de signature.

RSA / ECDSA (RS256, RS384, RS512, ES256, ES384, ES512)

La vérification asymétrique ne nécessite qu’une clé publique, qui par définition n’est pas secrète. C’est le cas facile pour les outils en ligne — les données qui transitent ne sont pas sensibles en elles-mêmes. La seule chose que vous devez protéger est le jeton, qui contient les mêmes revendications quel que soit l’algorithme.

En attendant que remove.sh livre la vérification RSA/ECDSA dans le décodeur, l’option locale la plus propre est step crypto :

step crypto jwt verify \
  --jwks https://your-issuer.example.com/.well-known/jwks.json \
  --iss https://your-issuer.example.com \
  --aud your-audience < token.txt

Ou avec openssl et une clé publique au format PEM :

# Extract the signed portion (header.payload)
SIGNED=$(cut -d. -f1,2 token.txt)

# Extract the signature, decode Base64URL
cut -d. -f3 token.txt | tr '_-' '/+' | base64 -d > sig.bin

# Verify with openssl
echo -n "$SIGNED" | openssl dgst -sha256 -verify pubkey.pem -signature sig.bin

Les deux fonctionnent hors ligne. Les deux vous permettent de vérifier un jeton sans jamais le coller dans un site web.

Décoder un JWT localement avec la CLI

Pour des inspections ponctuelles loin de tout navigateur, deux lignes de commande couvrent la plupart des cas.

Affichage formaté de l’en-tête :

cut -d. -f1 token.txt | tr '_-' '/+' | base64 -d 2>/dev/null | jq .

Affichage formaté de la charge utile :

cut -d. -f2 token.txt | tr '_-' '/+' | base64 -d 2>/dev/null | jq .

L’étape tr '_-' '/+' convertit le Base64URL en Base64 standard, ce que la plupart des décodeurs attendent. Le 2>/dev/null avale l’avertissement base64: invalid input qui apparaît lorsque le segment n’est pas paddé — jq affichera quand même le JSON correctement.

Si vous n’avez pas jq, le JSON Formatter de remove.sh embellit la sortie de l’une ou l’autre commande dans votre navigateur sans rien envoyer ailleurs. Redirigez vers votre presse-papiers avec pbcopy (macOS) ou xclip -sel clip (Linux), puis collez.

Comment partager un JWT en toute sécurité dans un rapport de bug

Une raison fréquente pour laquelle les développeurs se tournent vers jwt.io n’est pas le débogage — c’est le collage d’une charge utile décodée dans un fil Slack ou un ticket pour qu’un coéquipier puisse voir les revendications. Il existe une recette plus sûre.

  1. Décodez le jeton localement avec l’une des méthodes ci-dessus.
  2. Copiez uniquement le JSON décodé — jamais la forme encodée eyJhbG.... La forme encodée est l’identifiant ; le JSON décodé n’est que des données.
  3. Caviardez les revendications identifiantes avant de partager : remplacez sub, email, name, les ID utilisateur personnalisés et les ID de tenant par des valeurs de remplacement (sub: "user-redacted").
  4. Supprimez le segment de signature. Il ne peut pas être reconstruit à partir de l’en-tête et de la charge utile sans le secret ou la clé privée, ce qui signifie que le JSON caviardé n’est plus un identifiant utilisable.
  5. Pour les fixtures de test, générez un nouveau jeton signé avec un secret jetable. C’est la forme qui compte pour reproduire un bug, pas les valeurs.

Si un coéquipier doit confirmer « oui, c’est le même jeton que je vois de mon côté » sans que l’un ou l’autre ne partage le jeton lui-même, collez le JWT encodé dans le Hash Generator de remove.sh et échangez plutôt l’empreinte SHA-256. Même empreinte signifie même jeton ; l’empreinte ne révèle rien sur les revendications ou la signature.

Cette approche transforme un échange d’identifiants en un échange de données, ce qui est ce que votre politique de sécurité veut réellement.

Foire aux questions

jwt.io est-il open source ?

Le code frontend du site est publié sur GitHub, mais « open source » ne vous dit pas ce qui s’exécute sur jwt.io aujourd’hui. Comme l’a noté Jamie Tanna, « nous n’avons aucune idée si le code source… est réellement utilisé » sur le site en ligne. La vérifiabilité — un onglet Réseau qui reste vide — est un signal plus fort qu’un dépôt public.

jwt.io a-t-il déjà divulgué des jetons ?

Il n’y a pas de divulgation publique d’un incident de fuite de jetons. Il y a une reconnaissance publique des mainteneurs (GitHub n°700) que la gestion historique via paramètres de requête d’URL a créé des préoccupations de confidentialité suffisamment graves pour motiver une réécriture. L’absence d’incident connu n’équivaut pas à l’absence de risque.

Puis-je auto-héberger jwt.io ?

Oui, le code source est disponible, mais l’auto-hébergement ne résout le problème de confiance réseau que si vous auditez réellement le build et que vous le servez depuis une infrastructure que vous contrôlez. Pour la plupart des équipes, un outil côté client déjà vérifié représente moins de travail et moins de risque que d’exécuter un fork.

Le décodage d’un JWT nécessite-t-il le secret ?

Non. Décoder l’en-tête et la charge utile ne nécessite que le décodage Base64URL et l’analyse JSON — toutes deux des opérations purement locales. Le secret n’est nécessaire que pour vérifier la signature, ce qui est une étape distincte. Tout outil qui vous demande votre secret juste pour afficher les revendications décodées fait quelque chose de mal.

Pourquoi mon jeton apparaît-il comme expiré alors que je viens de le générer ?

La revendication exp est comparée à l’horloge locale. Les décodeurs s’exécutant dans un navigateur utilisent l’horloge de votre système ; les outils CLI utilisent l’horloge de l’hôte. Une horloge décalée — courante dans les VM et les exécuteurs CI — fera apparaître des jetons valides comme expirés et vice versa. Synchronisez votre horloge (sudo sntp -sS time.apple.com ou sudo ntpdate pool.ntp.org) et revérifiez.

Est-il réellement sûr de coller un jeton dans un outil dont vous avez vérifié qu’il est côté client ?

Oui, avec une réserve. Un outil qui est côté client aujourd’hui pourrait ne pas l’être demain. La vérification de l’onglet Réseau doit être répétée lorsque vous confiez à un outil un jeton particulièrement sensible, surtout après un long intervalle. Mettre l’outil en favori, ou utiliser une extension de navigateur qui alerte sur les requêtes sortantes d’une origine spécifique, rend cela moins coûteux.

Comment cela s’inscrit-il dans la position plus large de remove.sh sur la confidentialité ?

La même architecture s’applique à tous les outils pour développeurs du site. L’argumentaire long est dans Client-Side Developer Tools: The Privacy-First Approach, qui couvre en détail les formateurs JSON, les générateurs de hash, les décodeurs Base64 et l’API Web Crypto.

En résumé

jwt.io est une commodité populaire qui gère une catégorie de données — les identifiants — pour laquelle « la commodité populaire » n’est pas un modèle de confiance suffisant. Ses propres mainteneurs ont écrit, publiquement, que le site historique présente des problèmes de confidentialité suffisamment graves pour justifier une refonte. Le remplacement n’est pas la refonte de jwt.io ni un concurrent unique ; c’est une habitude.

L’habitude est : avant qu’un outil ne voie un jeton, exécutez le test du mode avion. S’il fonctionne hors ligne, il est local. Sinon, trouvez-en un qui fonctionne — ou repliez-vous sur une ligne de commande CLI.

Le JWT Decoder de remove.sh est conçu pour passer ce test. Ouvrez-le, désactivez votre réseau et décodez un jeton. S’il fonctionne toujours — et il le fera — vous venez de vérifier, de vos propres yeux, qu’aucun tiers ne verra jamais ce JWT.

C’est la réponse à « alternative jwt.io confidentialité ». Pas une autre marque. Une propriété vérifiable.