Vous subissez une attaque ? Faire appel à notre réponse aux incidents

info@angelforce.eu
Développement sécurisé

Sécurité des JWT : les pièges courants et comment les éviter

Erreurs JWT courantes (alg none, confusion RS256/HS256, secrets HMAC faibles, claims non vérifiés, injection via kid, révocation, stockage) et correctifs.

Aussi en :EnglishTürkçeDeutschEspañolItalianoPolskiNederlandsEesti

Illustration : Sécurité des JWT : les pièges courants et comment les éviter

Les JSON Web Tokens (JWT) sont très utilisés pour l’authentification des API et l’authentification unique (SSO). Le format est simple, mais il laisse de nombreuses décisions de sécurité au développeur et à la bibliothèque. La RFC 8725, qui rassemble les bonnes pratiques actuelles pour les JWT, recense les erreurs connues et la manière de les éviter ; cet article passe en revue celles que nous vérifierions en premier.

Un JWT est signé, pas secret

Un JWT signé, défini par la RFC 7519, comporte trois parties séparées par des points : un en-tête, une charge utile contenant des claims et une signature. Les deux premières sont seulement encodées en base64url. Quiconque détient le jeton peut les lire. Voici un exemple décodé :

{
  "alg": "RS256",
  "typ": "at+jwt",
  "kid": "2026-10-key-1"
}
{
  "iss": "https://auth.example.eu",
  "sub": "user-4821",
  "aud": "api.example.eu",
  "iat": 1792140000,
  "exp": 1792140900,
  "role": "editor"
}

La signature prouve que les claims n’ont pas été modifiés. Elle ne les cache pas. La RFC 7519 précise que, lorsqu’un jeton contient des informations sensibles du point de vue de la vie privée, des mesures doivent être prises pour empêcher leur divulgation, par exemple en utilisant un JWT chiffré (JWE). En pratique, n’y mettez ni données personnelles, ni identifiants internes que vous ne publieriez pas, ni quoi que ce soit de secret.

Piège 1 : accepter "alg": "none"

La RFC 7519 autorise des JWT « non sécurisés » avec "alg": "none" et une signature vide. La RFC 8725 décrit des bibliothèques qui acceptaient ces jetons et sautaient purement et simplement la vérification de la signature. Un attaquant pouvait alors y écrire les claims de son choix.

La RFC 7518 dispose que les implémentations ne doivent pas accepter les jetons non sécurisés par défaut, et l’OWASP REST Security Cheat Sheet recommande de ne jamais les autoriser. Vérifiez que votre bibliothèque rejette none, et testez-la avec un jeton forgé.

Piège 2 : laisser le jeton choisir l’algorithme

La valeur alg de l’en-tête est choisie par l’auteur du jeton, qui peut être un attaquant. La RFC 8725 décrit l’attaque classique par confusion : un serveur attend du RS256, vérifié avec une clé publique RSA. L’attaquant remplace alg par HS256 et signe le jeton en HMAC, en utilisant cette clé publique comme secret. Une bibliothèque qui suit l’en-tête le vérifie avec succès, et la clé publique est, par définition, publique.

La solution consiste à fixer l’algorithme côté serveur. L’OWASP précise que la vérification ne doit pas s’appuyer sur l’en-tête du JWT pour choisir l’algorithme. La RFC 8725 ajoute que chaque clé doit être utilisée avec un seul algorithme. Dans le code, cela ressemble à ceci :

verify(token,
       key        = issuer_public_key,
       algorithms = ["RS256"],
       issuer     = "https://auth.example.eu",
       audience   = "api.example.eu")

Piège 3 : des secrets HMAC faibles

Avec HS256, quiconque intercepte un jeton peut tenter de deviner le secret hors ligne, aussi vite que son matériel le permet. La RFC 8725 dispose que des mots de passe mémorisables par un humain ne doivent pas être utilisés directement comme clés HMAC. La RFC 7518 exige une clé au moins aussi longue que la sortie de la fonction de hachage, soit 256 bits pour HS256.

openssl rand -base64 32

Cette commande produit 32 octets aléatoires, soit 256 bits. Stockez-les dans un gestionnaire de secrets, pas dans le dépôt de code.

Les secrets partagés ont une seconde faiblesse. Tout service capable de vérifier un jeton HMAC peut aussi en créer un. C’est pourquoi l’OWASP préfère les signatures aux MAC : si plusieurs services valident des jetons, un seul service compromis pourrait sinon émettre des jetons pour tous les autres. Les algorithmes asymétriques comme RS256 ou ES256 permettent de ne partager que la clé publique.

Piège 4 : ne pas valider les claims

Une signature valide prouve seulement qui a émis le jeton. Ce sont les claims qui déterminent si ce service doit l’accepter maintenant.

  • exp : selon la RFC 7519, le jeton ne doit pas être accepté à partir de cette heure. Une petite tolérance pour le décalage d’horloge, généralement pas plus de quelques minutes, est admise.
  • nbf : s’il est présent, rejetez le jeton avant cette heure.
  • iss : vérifiez que l’émetteur est celui auquel vous faites confiance et qu’il détient la clé de signature.
  • aud : selon la RFC 7519, si le service ne se trouve pas dans l’audience, le jeton doit être rejeté.

L’OWASP recommande d’exiger et de valider par défaut iss, aud et exp pour les jetons d’accès aux API. La RFC 8725 explique pourquoi l’audience compte : sans elle, un jeton émis pour un service peut être rejoué auprès d’un autre qui fait confiance au même émetteur. Elle recommande aussi un typage explicite avec l’en-tête typ, afin qu’un type de jeton ne puisse pas être utilisé là où un autre est attendu. Pour les jetons d’accès OAuth, la RFC 9068 utilise at+jwt, comme dans l’exemple ci-dessus.

Piège 5 : faire confiance à kid, jku et x5u

Les paramètres d’en-tête qui indiquent au serveur où trouver la clé sont eux aussi contrôlés par l’attaquant. La RFC 8725 en liste les risques :

  • kid (identifiant de clé), utilisé dans une recherche en base de données ou dans un annuaire, peut permettre une injection SQL ou LDAP. Il en va de même s’il sert à construire un chemin de fichier.
  • jku et x5u pointent vers une URL où récupérer les clés. Suivre n’importe quelle URL ouvre la voie à la falsification de requêtes côté serveur (SSRF), et un attaquant pourrait la faire pointer vers sa propre clé.

Traitez kid comme une étiquette opaque et recherchez-le dans une liste fixe de clés que vous contrôlez. Ne récupérez des clés qu’à partir d’une liste d’URL autorisées ; la RFC 8725 ajoute que ces requêtes ne doivent pas transmettre de cookies.

Piège 6 : des jetons de longue durée sans révocation

Un JWT reste valide jusqu’à son expiration, même après la déconnexion de l’utilisateur ou la désactivation du compte par un administrateur. L’OWASP relève que les jetons peuvent survivre à la session qu’ils représentent.

  • Gardez des jetons d’accès de courte durée, qui se comptent en minutes plutôt qu’en jours.
  • Utilisez des jetons de rafraîchissement que le serveur vérifie à chaque utilisation et peut révoquer.
  • Pour les déconnexions et les suspensions de compte, tenez une liste de refus des identifiants de jetons (jti) jusqu’à leur expiration, comme le suggère l’OWASP.
  • Changez les clés de signature selon un calendrier, et disposez d’une procédure testée pour un renouvellement d’urgence.

Piège 7 : stocker les jetons là où les scripts peuvent les lire

Les jetons conservés dans localStorage peuvent être lus par n’importe quel JavaScript de la page. L’OWASP HTML5 Security Cheat Sheet prévient qu’une seule faille de cross-site scripting (XSS) suffit à voler tout le contenu de ces espaces de stockage, et déconseille d’y conserver des identifiants de session.

Pour les applications web, un cookie HttpOnly et Secure, avec un réglage SameSite adapté, tient le jeton hors de portée des scripts. Les cookies exposent toutefois à la falsification de requêtes intersites (CSRF) : prévoyez aussi cette protection. Quel que soit votre choix, une Content Security Policy stricte réduit d’emblée le risque de XSS.

Liste de contrôle pour la validation

  1. Fixez côté serveur les algorithmes acceptés, et rejetez none.
  2. Utilisez une bibliothèque qui vérifie la signature avant toute lecture des claims.
  3. Validez exp, nbf, iss et aud à chaque requête.
  4. Utilisez typ ou des audiences distinctes pour séparer les types de jetons.
  5. Recherchez les clés par kid dans un ensemble fixe ; ne récupérez jamais de clés depuis des URL arbitraires.
  6. Utilisez des clés HMAC aléatoires d’au moins 256 bits, ou des clés asymétriques.
  7. Ne mettez dans la charge utile ni secrets ni données personnelles inutiles.
  8. Gardez des jetons d’accès de courte durée et prévoyez leur révocation.
  9. Stockez les jetons côté navigateur hors de portée de JavaScript.

Par où commencer

Collez un jeton de votre environnement de test dans notre décodeur JWT pour voir exactement ce qu’il révèle, et comparez ses valeurs alg, aud et exp à cette liste. Évitez de coller des jetons de production en cours de validité dans un outil en ligne, quel qu’il soit.

Si vous souhaitez un regard extérieur sur la conception de votre authentification ou un audit du code qui gère vos jetons, notre équipe de développement sécurisé peut vous aider.

Comment nous pouvons vous aider

Développement sécurisé

Nous intégrons la sécurité aux logiciels dès le premier commit, et nous développons nos propres outils de sécurité.

Décodeur JWT

Décodez un JSON Web Token et repérez les erreurs de sécurité courantes, sans qu’il quitte votre navigateur.

Nous contacter
Équipe AngelForce

Rédigé par l’équipe AngelForce : des consultants en sécurité certifiés CEH, LPT Master et CISA qui testent, surveillent et protègent les systèmes d’organisations dans toute l’Europe et en Turquie.

Rédigé avec l’aide de l’IA à partir des sources publiques indiquées sur cette page, puis relu par l’équipe AngelForce avant publication.

Sources

  1. datatracker.ietf.org/doc/html/rfc7519
  2. datatracker.ietf.org/doc/html/rfc8725
  3. datatracker.ietf.org/doc/html/rfc7518
  4. cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html
  5. cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html

Articles : Développement sécurisé

Dites-nous ce que vous devez protéger

Décrivez votre environnement et vos objectifs. Nous vous répondrons par e-mail.

Nous contacter
info@angelforce.eu

AngelForce OÜ · Tallinn, Estonie