Introduction
Lors du développement d’une application web, la question de l’authentification est essentielle. Selon les technologies utilisées, celle-ci peut être « stateful » ou « stateless ».
Dans un modèle stateful, l’authentification repose sur la gestion de sessions côté serveur. Lorsqu’un utilisateur se connecte, le serveur génère un identifiant de session unique. Cet identifiant est ensuite envoyé avec chaque requête afin que le serveur puisse retrouver les informations liées à l’utilisateur. C’est donc le serveur qui conserve l’état de la session tout au long de la navigation.
À l’inverse, dans une approche stateless, c’est le client — souvent le navigateur — qui stocke les informations de session, généralement sous la forme d’un jeton signé ou chiffré. À chaque requête, ce jeton est transmis au serveur, qui peut alors le vérifier et en extraire les données nécessaires. Le serveur ne garde aucune trace de session, d’où le terme stateless. Ce mode d’authentification est notamment utilisé avec des jetons JWT (JSON Web Tokens).
De plus en plus utilisés, les JWT ne sont cependant pas exempts de vulnérabilités ; et une mauvaise implémentation peut exposer les applications à des attaques potentiellement désastreuses.
Dans cet article, nous détaillerons le fonctionnement des JWT. Nous examinerons également les vulnérabilités courantes associées, les techniques d’attaques ainsi que les bonnes pratiques de configuration et d’implémentation pour minimiser ces risques.
Guide complet sur le standard JWT (JSON Web Token)
- Fonctionnement d'un JWT
- Avantages et inconvénients des JWT
- Vulnérabilités et exploitations courantes des JWT
- Comment sécuriser l'implémentation des JWT ?
- Conclusion
Fonctionnement d’un JWT
Qu’est-ce qu’un JWT ?
Comme précisé dans la RFC 7519, un JSON Web Token (JWT) est un format compact permettant de représenter des informations, appelées « claims », échangées entre deux parties. Ces claims sont encapsulés dans un objet JSON qui constitue le contenu (ou payload) d’une structure signée (JWS – JSON Web Signature) ou chiffrée (JWE – JSON Web Encryption).
Autrement dit, le JWT est une norme qui peut être implémentée de deux façons : soit via un JWS, soit via un JWE.
Dans le cas du JWS, les données ne sont pas chiffrées mais signées numériquement. Cela garantit leur intégrité : le contenu reste lisible, mais seul un détenteur du secret peut générer une signature valide. C’est la forme la plus couramment utilisée du JWT.
À l’inverse, un JWE chiffre entièrement son contenu. Les informations qu’il contient ne sont accessibles qu’aux entités disposant de la clé de déchiffrement.
Étant donné que le JWS est de loin l’implémentation la plus répandue, la suite de cet article s’appuiera sur ce format. Par souci de simplicité, nous utiliserons le terme JWT même lorsqu’il s’agira en réalité d’un JWS.
Les JWT sont générés par le serveur lorsqu’un utilisateur s’authentifie sur une application web. Une fois émis, ces jetons sont envoyés au client, qui les joint automatiquement à chaque requête HTTP suivante. Cela permet au serveur d’identifier l’utilisateur à chaque interaction, sans nécessiter de nouvelle authentification.
Le jeton contient des informations encodées et signées à l’aide d’une clé privée détenue par le serveur. À chaque réception d’un JWT, le serveur peut vérifier son authenticité en recalculant la signature à l’aide de sa clé et en la comparant à celle présente dans le jeton. Si les deux signatures concordent, le JWT est considéré comme valide.
Structure et génération d’un JWT
Prenons le JWT suivant :
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaXNBZG1pbiI6ZmFsc2V9.EypViEDiJhjeuXgjtGdibxrFPFZyYKn-KqFeAw3c2No
Ce type de JWT est constitué de trois parties distinctes :
- L’en-tête JOSE (JSON Object Signing and Encryption) :
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
- Le payload :
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaXNBZG1pbiI6ZmFsc2V9
- La signature :
EypViEDiJhjeuXgjtGdibxrFPFZyYKn-KqFeAw3c2No
Chaque partie est séparée d’un « . » avec l’en-tête JOSE et le payload encodés en base64url. La signature, quant à elle, est générée en fonction d’un secret ou d’une paire de clés cryptographiques.
Décodons maintenant ce JWT avec CyberChef par exemple.
En-tête JOSE
{
"alg": "HS256",
"typ": "JWT"
}
L’en-tête JOSE d’un JWT contient notamment deux paramètres clés : alg
et typ
. Le paramètre alg
indique l’algorithme de signature utilisé, tandis que typ
précise le type de jeton. Seul alg
est obligatoire, mais d’autres paramètres optionnels peuvent également être ajoutés selon les besoins. Certains d’entre eux seront abordés plus loin dans cet article.
Payload
{
"sub": "1234567890",
"name": "John Doe",
"isAdmin": false
}
Le payload d’un JWT contient ce que l’on appelle les claims, c’est-à-dire les informations que le jeton transporte. Dans notre exemple, on retrouve les claims sub
, name
et isAdmin
.
On distingue trois types de noms de claims :
- Les claims enregistrés par la RFC 7519 (comme
sub
) - Puis, les claims publics, standardisés et recensés dans le registre IANA « JSON Web Token Claims » pour éviter les collisions de noms
- Enfin, les claims privés, définis librement par les développeurs selon les besoins de l’application
Dans notre cas :
sub
(pour Subject) est un claim standard de la RFC 7519. Il désigne l’entité principale à laquelle le JWT se rapporte (souvent l’identifiant de l’utilisateur).name
est un claim public, utilisé ici pour indiquer le nom de l’utilisateur.isAdmin
est un claim privé, ajouté par le développeur pour signaler si l’utilisateur dispose ou non de droits administrateurs sur la plateforme.
Il existe de nombreux autres claims définis dans la RFC, mais aucun n’est strictement obligatoire. Pour en savoir plus, vous pouvez consulter la section dédiée de la RFC 7519.
Signature
Le rôle crucial de la signature dans la sécurité des JWT
La signature est l’élément qui confère toute sa sécurité au JWT. Elle est calculée à partir de l’en-tête JOSE, du payload et d’un secret connu uniquement du serveur.
Grâce à ce mécanisme, toute modification du jeton (par exemple par un attaquant tentant de falsifier les données) entraînerait une signature invalide. Le serveur, en recalculant la signature avec le secret, détectera alors l’incohérence et rejettera le JWT. C’est pourquoi il est crucial que ce secret reste confidentiel : s’il venait à fuiter, un attaquant pourrait générer des jetons frauduleux parfaitement valides.
Algorithmes de signature : symétriques (HS256) vs asymétriques (RS256)
La signature peut être générée à l’aide de différents algorithmes définis dans la spécification JWA – JSON Web Algorithms. Selon l’algorithme utilisé, le secret peut être :
- Symétrique, avec une même clé partagée pour la signature et la vérification (ex. :
HS256
) - Asymétrique, avec un couple clé privée / clé publique (ex. :
RS256
)
Dans le cas d’une clé asymétrique, la clé publique peut être exposée par le serveur sous un format standardisé défini dans la spécification JWK – JSON Web Key. Cela permet à des tiers de vérifier la validité de la signature, sans jamais accéder à la clé privée.
Prenons un premier exemple : un JWT signé à l’aide d’une clé symétrique et de l’algorithme HS256. Dans ce cas, la signature est générée de la manière suivante :
HMACSHA256(base64UrlEncode(<en-tête JOSE>) + "." + base64UrlEncode(<payload>), "aVerySecretSecret")
Le résultat est ensuite encodé en base64url et ajouté à la fin du JWT. Le serveur, en possession du même secret, pourra recalculer cette signature à la réception du jeton et la comparer à celle fournie. Si les deux correspondent, l’intégrité et l’authenticité du jeton sont confirmées.
Bien entendu, dans un cas réel, il est essentiel d’utiliser un secret robuste, difficile à deviner ou à brute forcer.
En second exemple, il est également possible de signer un JWT à l’aide d’un couple clé privée / clé publique, en utilisant l’algorithme RS256 (basé sur RSA) :
RSASHA256(base64UrlEncode(<en-tête JOSE>) + "." + base64UrlEncode(<payload>), <clé privée>)
L’intérêt ici est que seul le serveur possédant la clé privée peut générer une signature valide, mais la vérification peut être déléguée à d’autres services via la clé publique, ce qui est particulièrement utile dans les architectures distribuées ou en microservices.
Avantages et inconvénients des JWT
Utiliser un JWT à la place d’un identifiant de session présente certains avantages, mais aussi des inconvénients notables.
Parmi les points positifs, on retrouve avant tout l’interopérabilité : un JWT peut facilement être transmis entre plusieurs applications, même si elles utilisent des technologies différentes. Contrairement à une session classique, il n’est pas nécessaire de stocker d’état côté serveur, ce qui simplifie la gestion. Il suffit de vérifier la signature du jeton pour authentifier l’utilisateur, sans avoir à interroger une base de données ou un système de session.
De plus, le format du JWT (JSON encodé en base64url) en fait un moyen compact et lisible pour transporter des données, souvent utilisé dans les architectures distribuées ou les API.
Mais ce fonctionnement stateless a aussi un inconvénient majeur : on ne peut pas invalider un JWT avant sa date d’expiration. Si jamais un jeton est compromis ou qu’un utilisateur doit être déconnecté immédiatement, il faudra soit :
- mettre en place une liste noire (blacklist) côté serveur (ce qui réintroduit une forme d’état),
- soit attendre que le JWT expire, ce qui peut poser des problèmes de sécurité.
Quelles sont les vulnérabilités et les exploitations courantes des JWT ?
Pour un attaquant, compromettre un JWT peut être particulièrement intéressant, car cela revient à compromettre tout le système d’authentification de l’application. En effet, si l’attaquant parvient à manipuler un JWT valide, il peut modifier son identité, élever ses privilèges, voire accéder aux comptes d’autres utilisateurs.
Normalement, une telle attaque est impossible grâce au mécanisme de signature, qui garantit l’intégrité du jeton. Mais si l’application est mal configurée ou vulnérable, un attaquant pourra exploiter certaines failles pour modifier le contenu du JWT sans que cela ne soit détecté.
Dans cette partie, nous allons examiner plusieurs vulnérabilités courantes liées à la gestion des JWT, et comment elles peuvent conduire à des attaques réussies.
Pour illustrer ces attaques, nous utiliserons :
- le site jwt.io pour visualiser et manipuler facilement les jetons,
- et l’extension Burp Suite « JWT Editor » pour générer ou modifier des clés et secrets dans un contexte de test.
Défaut de vérification de la signature du JWT
L’un des premiers tests à effectuer sur un JWT consiste à vérifier si la signature est réellement vérifiée par le serveur. Il arrive, dans certains cas, que le développeur commette une erreur critique : ne pas vérifier la signature et se contenter de décoder le JWT pour en lire le contenu.
Si cette vérification est absente, il devient alors trivial pour un attaquant de modifier le contenu du jeton, sans que le serveur ne s’en rende compte. Par exemple, si le payload contient un claim tel que role
, l’attaquant peut simplement changer sa valeur pour s’attribuer des privilèges administrateur, et ce, sans avoir besoin d’une signature valide.
C’est une erreur de sécurité grave, mais malheureusement encore rencontrée lors de nos pentests web.
Vulnérabilités inhérentes à l’algorithme « none »
Comprendre les principes et les risques liés à l’algorithme « none »
En consultant la RFC 7518, qui définit les algorithmes utilisables pour signer un JWT, on découvre une particularité surprenante : la présence d’un algorithme nommé none
.
Lorsque l’algorithme none
est utilisé, le JWT n’est pas signé du tout, et il est alors considéré comme valide par défaut. Cela représente un risque majeur si l’application Web utilise une bibliothèque qui autorise implicitement l’usage de cet algorithme.
Dans un tel cas, un attaquant peut simplement modifier l’en-tête JOSE du JWT, remplacer le champ alg
par none
, puis altérer librement le contenu du jeton (par exemple, en se donnant des privilèges administrateur). Le tout sans avoir besoin d’une signature valide.
Ce type d’attaque est possible car l’en-tête est interprété avant même que la signature n’ait pu être vérifiée, ce qui ouvre une porte aux manipulations si l’application n’effectue pas de contrôle rigoureux sur les algorithmes acceptés.
Par exemple, admettons qu’une application web renvoie le JWT suivant lorsque l’utilisateur « johndoe » se connecte :
Même si l’application vérifie correctement la signature du JWT, un comportement dangereux peut persister : elle accepte l’algorithme none
.
Cela signifie que si un attaquant modifie l’en-tête du JWT pour y indiquer "alg": "none"
et supprime la signature, le serveur acceptera malgré tout le jeton comme valide.
Une fois cette étape franchie, le payload peut être modifié librement, permettant par exemple d’attribuer un rôle administrateur à un utilisateur malveillant. Ce type de faille repose donc sur l’absence de vérification stricte de l’algorithme utilisé.
Pour l’application, le JWT suivant est donc valide :
Comment se protéger : bannir « none » ou autoriser explicitement les bons algorithmes
Pour contrer cette vulnérabilité sans changer de librairie, certains développeurs mettent en place une liste noire interdisant l’utilisation explicite de l’algorithme none
.
Mais cette approche reste fragile : rien n’empêche un attaquant de tenter des variantes comme NonE
ou NoNe
, susceptibles de contourner des vérifications mal implémentées.
C’est pourquoi l’utilisation d’une liste noire est déconseillée. Il est préférable d’adopter une liste blanche, en définissant explicitement les algorithmes autorisés (comme HS256
ou RS256
). Cela garantit un contrôle strict et évite les failles dues à des interprétations laxistes de l’algorithme.
Secret faible et attaques brute force
Même si une application Web vérifie correctement la signature du JWT et rejette l’algorithme none
, la vigilance reste de mise : un JWT peut encore présenter des vulnérabilités.
Prenons le cas d’un JWT signé avec l’algorithme HS256. Ce dernier repose sur une clé secrète partagée, utilisée à la fois pour signer et pour vérifier le token. Cela signifie que si le secret est trop simple, prévisible, ou mal protégé, un attaquant peut tenter de le retrouver par brute force, en testant une large liste de secrets communs.
Dès qu’un des secrets testés génère une signature identique à celle du JWT d’origine, l’attaquant a réussi : il peut alors générer et signer ses propres tokens valides, et ainsi se faire passer pour n’importe quel utilisateur auprès de l’application.
Par exemple, considérons le JWT suivant :
Pour brute forcer le secret utilisé pour signer ce JWT, il est possible d’utiliser l’outil « hashcat » avec des listes de secrets JWT connus.
Dans notre cas, avec la commande suivante on trouve le secret qui a été utilisé pour signer notre JWT :
hashcat -a 0 -m 16500 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImpvaG5kb2UiLCJlbWFpbCI6ImRvZWpAZXhhbXBsZS5jb20iLCJpYXQiOjE3NDQwMzM0MDAsImV4cCI6MTc0NDExOTgwMH0.If5smKFyDhdY5fPN-qQOawSREJIQxRLRslQeUYTn070 /usr/share/seclists/Passwords/scraped-JWT-secrets.txt
L’option -a 0
indique le mode d’attaque utilisé par Hashcat. Ici, la valeur 0
correspond à une attaque par dictionnaire, c’est-à-dire l’approche par défaut. L’option -m 16500
précise le type de hash à attaquer — dans ce cas, le type 16500
correspond à la signature d’un JWT.
Il suffit ensuite de fournir le JWT à tester ainsi qu’une liste de secrets à essayer.
Une fois la recherche terminée et le secret trouvé, on peut afficher le résultat en utilisant l’option --show
.
En vert, on retrouve le secret utilisé pour signer le JWT.
Avec ce secret, l’attaquant peut forger autant de JWT qu’il souhaite.
Injection de paramètres spécifiques dans l’en-tête JOSE du JWT
Dans certains cas, il est possible de manipuler ou ajouter des paramètres dans l’en-tête JOSE pour exploiter le JWT. Ces paramètres sont spécifiés dans la RFC 7515.
jwk
Il arrive que la clé publique du JWT soit directement incluse dans l’en-tête. Dans ce cas, la clé est au format JWK (JSON Web Key) et est placée dans le paramètre jwk
de l’en-tête. Une clé publique au format JWK est représentée sous forme d’un objet JSON. Voici un exemple :
{
"kty": "RSA",
"e": "AQAB",
"kid": "09979990-6f43-4b28-acf1-a99dcb455c9f",
"n": "u1bmuBchhoNbOuEYeyEjE_sOTng7boN7hdbcnQoNQNheSQCOwGcfZXE8vpFFdIFY6zVm8loYok6wEUtq-JjDj2jFrj68asuJrFbvAyC4M6FJhP6Ox4K4UzUQlLBEJvmbFzU-CfjyqV9xPR1q09Bg9Qc3qNeg7UYfXVgniFm5CkVjlpvtn1Xj6UHLWQ1NAqknYKcB13S4vNdAEyx69PFaRVjco9PzbJefubaZ78YpRrMKEvknim1bH1XHCj-JKb8enhgf78J4uTwG6CMvunVkY1KKbZI-AJjnRprAYHW26_fQg5GJkD13taTGSkNtpzrji6IY4ls-3z7Zz-IPpA4iHQ"
}
Cependant, si le serveur ne met pas en place de liste blanche des clés publiques autorisées, un attaquant peut signer le JWT avec sa propre clé privée et intégrer sa clé publique dans le paramètre jwk
, associée à cette clé privée.
Prenons le JWT suivant :
Ce JWT est signé avec l’algorithme RS256, utilisant un couple clé privée/public. La clé privée est connue uniquement du serveur. Cependant, dans ce scénario, le serveur accepte l’utilisation du paramètre jwk
et permet l’ajout de n’importe quelle clé publique. Un attaquant peut donc :
- Modifier le JWT (par exemple, changer la valeur du claim
username
de « johndoe » à « admin »). - Ajouter sa propre clé publique dans le paramètre
jwk
de l’en-tête. - Signer le JWT avec sa propre clé RSA privée.
Le JWT final ressemblera au suivant :
On peut observer l’intégration du paramètre jwk
, qui contient la clé publique de l’attaquant au format JWK. Il est également essentiel d’ajouter le paramètre kid
, qui spécifie quelle clé publique le serveur doit utiliser pour vérifier la signature. Dans ce cas, la valeur de kid
correspond à celle du jwk
, ce qui indique au serveur qu’il doit utiliser la clé publique incluse dans le paramètre jwk
.
Ainsi, ce JWT sera accepté par le serveur, et l’attaquant pourra accéder au compte administrateur.
jku
Paramètre jku et JWK Sets
La clé publique peut également être exposée sur un serveur externe. Dans ce cas, elle fait généralement partie d’une liste de clés publiques (appelée JWK Set), où chaque clé est associée à un identifiant unique, le kid
. Cette liste est souvent accessible via des endpoints tels que .well-known/jwks.json
ou /jwks.json
.
Voici un exemple :
Dans cet exemple, le JWK Set contient une seule clé publique, qui est présentée sous le format JWK dans un tableau JSON nommé keys
.
Pour indiquer au serveur quel JWK Set utiliser, il est possible d’ajouter le paramètre d’en-tête jku
dans le JWT, lequel spécifie l’URL du serveur exposant ce JWK Set.
Cependant, tout comme l’attaque sur le paramètre jwk
, si le serveur accepte le paramètre jku
sans mettre en place de liste blanche de clés publiques ni de domaines autorisés, un attaquant peut alors :
- Modifier le JWT.
- Héberger la clé publique associée à la clé privée qu’il utilise, sur son propre serveur.
- Ajouter le paramètre
jku
dans l’en-tête, avec comme valeur l’URL de son propre serveur. - Ajouter le paramètre
kid
pour pointer vers l’identifiant de la clé publique hébergée sur son serveur. - Signer le JWT avec sa propre clé privée.
Exploitation du jku
Reprenons le JWT précédent :
L’attaquant peut alors héberger la clé publique dans un JWK Set sur son propre serveur, comme montré dans l’exemple précédent. Il peut ensuite ajouter le paramètre jku
, pointant vers l’URL de son serveur, inclure le paramètre kid
et signer le JWT avec sa propre clé privée.
Le JWT final ressemblera au suivant :
Il est crucial que le kid
dans le JWT corresponde exactement à celui de la clé publique exposée sur le serveur, afin que ce dernier utilise la clé correcte dans le JWK Set. Une fois cette étape franchie, le JWT sera accepté par le serveur, permettant ainsi à l’attaquant d’obtenir l’accès au compte administrateur.
Il est également important de noter que, comme c’est le serveur qui effectue la requête pour récupérer la clé publique depuis un serveur externe, cela ouvre la porte à un risque de SSRF (Server-Side Request Forgery). Toutefois, cette exploitation spécifique ne sera pas détaillée dans cet article.
kid
Comme vous l’avez compris, le paramètre d’en-tête kid
indique au serveur quelle clé publique utiliser pour vérifier la signature du JWT. Peu importe l’emplacement de la clé publique, le serveur cherche généralement celle qui possède le même kid
que celui présent dans le JWT. Cependant, selon la manière dont le serveur gère la valeur du kid
, des vulnérabilités classiques telles que l’injection SQL ou le path traversal peuvent se manifester.
Dans l’exemple suivant, une vulnérabilité de path traversal est présente dans le paramètre kid
, et le JWT est signé avec un algorithme symétrique, HS256.
Pour modifier arbitrairement un JWT, l’attaquant peut exploiter le paramètre kid
en le dirigeant vers un fichier dont le contenu est connu, puis signer le JWT avec une clé symétrique dont la valeur correspond au contenu de ce fichier.
En prenant l’exemple du JWT précédent, l’attaquant peut d’abord utiliser une vulnérabilité de path traversal dans le paramètre kid
pour le rediriger vers le fichier /dev/null
. Ensuite, il crée une clé symétrique ayant pour valeur une chaîne de caractères vide. Puisque le kid
pointe vers une valeur vide, l’attaquant peut modifier le JWT à sa guise et le signer avec sa clé symétrique vide. Comme le fichier /dev/null
(et donc la clé symétrique) a une valeur vide, la signature du JWT sera valide.
Le JWT final pourra ressembler au suivant :
On peut voir que le JWT est signé avec un secret dont la valeur est vide. Ce dernier sera donc accepté par le serveur et l’attaquant aura un rôle « admin ».
Algorithm Confusion
Principes de l’attaque « Algorithm Confusion »
Une application devient vulnérable à une attaque de type « algorithm confusion » lorsque le développeur utilise la même clé publique pour vérifier la signature du JWT, que l’algorithme soit symétrique ou asymétrique.
Bien que cela puisse sembler peu probable, un développeur peut parfois supposer que l’algorithme utilisé sera toujours asymétrique, sans procéder à une vérification explicite.
Ce type de comportement peut être illustré par le pseudo-code suivant :
publicKey = <public-key-of-server>;
token = request.getCookie("jwt");
verify(token, publicKey);
Si le serveur permet à l’utilisateur de modifier l’algorithme dans le JWT, un attaquant peut alors choisir un algorithme symétrique et utiliser la clé publique comme secret.
Exploitation de la vulnérabilité
Pour exploiter cette vulnérabilité, l’attaquant doit suivre plusieurs étapes :
- Récupérer la clé publique
- Transformer la clé publique en un format valide
- Modifier les claims ciblés et l’algorithme du JWT
- Signer le JWT forgé avec la clé publique
Concernant la première étape, si la clé publique n’est pas exposée par le serveur (par exemple via l’endpoint /jwks.json
), il est possible de la calculer à partir de deux JWT générés. Des outils comme « rsa_sign2n » peuvent être utilisés à cet effet.
Quant à la deuxième étape, il est crucial de transformer la clé publique dans un format valide, car elle doit être exactement identique à celle utilisée par le serveur pour vérifier la signature.
Par exemple, si l’attaquant obtient la clé publique en format JWK mais que le serveur utilise un format X.509 PEM pour stocker et vérifier la clé, l’attaquant devra convertir la clé JWK en format X.509 PEM avant de signer son JWT.
Ces deux étapes sont réalisées par l’outil rsa_sign2n. Pour l’installer et l’utiliser, voici les différentes commandes à exécuter :
La dernière commande permet de calculer la clé publique à partir de deux JWT générés. De plus, l’outil retourne un ou plusieurs JWT signés en HS256 avec la clé publique calculée, au format X.509 PEM, afin de tester leur validité.
Si l’un des JWT est valide, cela indique que l’application Web est vulnérable à une attaque de type « algorithm confusion ». La prochaine étape consiste alors à modifier le JWT pour atteindre l’objectif souhaité et à le signer à nouveau avec la clé publique.
Avec CyberChef :
Ce JWT sera ensuite accepté par le serveur.
Comment sécuriser l’implémentation des JWT ?
Voici plusieurs bonnes pratiques à adopter pour sécuriser l’implémentation de vos JWT :
- Définir clairement la configuration du JWT : précisez l’algorithme à utiliser ainsi que les claims attendus dans le payload. Cette configuration doit être bien documentée.
- Éviter de réimplémenter la gestion des JWT : privilégiez l’usage de bibliothèques reconnues et bien maintenues dans le langage de programmation utilisé. Ces librairies gèrent la génération, la vérification des signatures et la manipulation des claims de manière sécurisée.
- Respecter la configuration définie : la bibliothèque choisie doit impérativement suivre la configuration initialement définie pour éviter toute incohérence qui pourrait introduire des vulnérabilités.
- Sécuriser l’usage du paramètre
jku
: si ce paramètre est utilisé, mettez en place une liste blanche de domaines autorisés côté serveur pour éviter les requêtes vers des sources non maîtrisées. - Toujours inclure le claim
exp
(Expiration Time) : cela permet de limiter la durée de validité du JWT et de réduire les risques en cas de compromission. - Utiliser des secrets distincts : si vous utilisez plusieurs JWT pour différents usages ou environnements, veillez à signer chacun avec un secret unique.
Conclusion
Dans cet article, nous avons exploré en détail le fonctionnement des JSON Web Tokens (JWT) : leur utilité, leur structure, leurs avantages, mais aussi leurs limites. Nous avons également identifié les vulnérabilités les plus courantes, les techniques d’exploitation possibles et les bonnes pratiques pour s’en prémunir.
Si le sujet vous a intéressé, je vous encourage vivement à mettre en pratique ces notions à travers des laboratoires interactifs comme ceux proposés par la Burp Academy. Ces labs sont non seulement de grande qualité, mais accompagnés d’un contenu pédagogique clair et approfondi.
Auteurs : Lorenzo CARTE – Pentester & Amin TRAORÉ – CMO @ Vaadata