Introduction
JWT (JSON Web Token) définit dans la RFC 7519 est un standard permettant la transmission d’information entre deux parties via l’utilisation d’objets JSON. JWT repose sur les standards JSON Web Signature (JWS) et JSON Web Encryption (JWE) permettant d’assurer l’intégrité et / ou la confidentialité des données transmises. En pratique, les jetons JWT sont principalement utilisés afin de sécuriser l’accès à des Web services REST ou encore comme solution de SSO.
Exemple d’utilisation des jetons JWT dans le cas d’un SSO :
L’utilisation de jetons JWT permet la mise en place d’application « stateless» et ainsi supprimer la dépendance des sessions. Ainsi la mise en place de ce type de jeton est souvent vue comme une protection contre les attaques CSRF.
Format
Les jetons JWT sont constitués de différentes parties chacune séparée par le caractère point « . ». Bien que le nombre de parties puisse varier, les jetons JWT sont souvent constitués de trois parties :
Figure 3 : Format d’un jeton JWT
- Entête : permet de déclarer que le format JWT est utilisé et de préciser l’algorithme (et potentiellement ses paramètres) utilisé pour la signature numérique et / ou le chiffrement.
{"alg": "HS256","typ": "JWT"}
- Charge utile : représente l’objet à transmettre au format JSON. Il peut posséder différents attributs / affirmations dont certains sont définis par la RFC (iss, sub, iat, exp…) :
{"sub": "1234567890","name": "anonymous","society": "Wavestone","email": "anonymous@wavestone.com","isadmin": false}
- Signature : cette partie permet au destinataire du jeton de pouvoir vérifier l’intégrité du message ainsi que l’identité de l’émetteur :
35cbb7559f29a26e1220983cc059965eadd005d6deeba450a05b70bcfe52eae3
La signature du message est calculée de la façon suivante :
key = 'laclesecrete'unsignedToken = encodeBase64(header) + '.' + encodeBase64(payload)signature = HMAC-SHA256(key, unsignedToken)
Ensuite, l’ensemble des parties sont encodés en base64 (en mode URL compatible et sans les caractères « = » de bourrage) et concaténée (séparées par le caractère « . ») pour être transmise dans les requêtes. Pour l’exemple précédent, le jeton JWT encodé est le suivant :
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6ImFub255bW91cyIsInNvY2lldHkiOiJXYXZlc3RvbmUiLCJlbWFpbCI6ImFub255bW91c0B3YXZlc3RvbmUuY29tIiwiaXNhZG1pbiI6ZmFsc2V9.Ncu3VZ8pom4SIJg8wFmWXq3QBdbe66RQoFtwvP5S6uM
Cryptographie
Afin d’assurer la sécurité (authentification, confidentialité, intégrité) des données transmises, le standard JWS s’appuie sur les standards JWS et JWE.
Signature
Le standard JWAdéfinit les algorithmes de signature numérique et MACs pouvant être utilisés, le standard JWT n’impose que le support des suivants :
- None
- HS256 : HMAC-SHA-256
Cependant, les algorithmes asymétriques suivants sont supportés par les principales implémentations :
- RS256: RSA (PKCS1-v1.5) et SHA-1
- ES256: ECDSA (P-256) et SHA-256
Chiffrement
Le support du chiffrement pour les jetons JWT est optionnel, les algorithmes les plus souvent supportés sont les suivants :
- RSA1_5 : RSA (PKCS1-v1_5) et AES
- A128CBC-HS256 : AES-CBC et HMAC-SHA2
Sécurité
Algorithme « none »
Alors que les algorithmes de chiffrement et de signature supportés sont majoritairement robustes, le support de la méthode « None » par les serveurs représente un réel risque. En effet, si cette méthode est considérée comme valide par le client JWT alors un attaquant est en capacité de forger ou d’altérer le contenu d’un message.
Dans l’exemple ci-dessus, il faudrait alors modifier l’entête :
{"alg": "none","typ": "JWT"}
Le jeton transmis serait le suivant :
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6ImFub255bW91cyIsInNvY2lldHkiOiJXYXZlc3RvbmUiLCJlbWFpbCI6ImFub255bW91c0B3YXZlc3RvbmUuY29tIiwiaXNhZG1pbiI6ZmFsc2V9.
Attaque par recherche exhaustive
L’ensemble de la sécurité des jetons JWT repose sur la confidentialité de la clé de chiffrement. Bien qu’aucune attaque efficace ne soit connue, un attaquant pourrait tenter de la récupérer par recherche exhaustive. Pour les modes HMAC, il est alors possible d’utiliser l’outil John jumbo afin d’effectuer ce type d’attaques en mode « hors connexion ».
Afin de faciliter ce type d’attaque, nous avons développé un script prenant en entrée un jeton JWT et fournissant en sortie un format utilisable par John jumbo.
Lors d’un test d’intrusion, nous avons également été surpris de pouvoir valider un jeton JWT obtenu auprès de l’application testée sur le site jwt.io :
Il se trouve que le secret utilisé par l’application pour signer les jetons avait une valeur par défaut, à savoir « secret »…. Importance du choix de l’algorithme de signature / chiffrement
Dans une architecture utilisant des jetons JWT, les clients ont besoins de connaitre :
- La clé partagée : si un algorithme symétrique est utilisé (HMAC, AES…) ;
- La clé publique : si un algorithme asymétrique est utilisé (RSA, ECDSA…).
Par conséquent, en cas d’utilisation d’un algorithme symétrique, les clients (service providers…) sont aussi en capacité de signer / chiffrer un jeton ; en cas de compromission d’un client par un attaquant, ce dernier pourrait alors tenter de compromettre la totalité de l’infrastructure (via la construction de jetons spécifiques). Ainsi, il est préférable d’utiliser des algorithmes de chiffrement asymétriques s’il n’est pas nécessaire que les clients (service providers…) puissent signer des jetons.
Vérification de la signature
La vérification de la signature est une étape sensible et doit ainsi être correctement implémentée. En 2015, une vulnérabilitépermettant sur l’implémentation des mécanismes de vérification de signature affectant de nombreux composants (node.js, pyjwt, php-jwt...) a ainsi été signalée par Tim McLean. Sous certaines conditions, cette vulnérabilité permettait alors à un attaquant d’utiliser une clé publique RSA afin de signer un jeton via HMAC et ainsi de pouvoir générer des jetons acceptés par les applications clientes.
Dangers du stockage local
Certaines applications conservent les jetons localement afin de pouvoir les utiliser ultérieurement via l’utilisation de code côté client (JavaScript…) notamment pour les applications « stateless » dont la taille des jetons est importante. Alors que les cookies sont protégés par les attributs « secure » et « httpOnly », les jetons manipulés par le JavaScript ne possèdent quant à eux aucun attribut de sécurité et sont ainsi vulnérable à des attaques de type vol de session (pas d’équivalent de « httpOnly ») ou encore transmission des jetons sur des canaux non sécurisés (pas d’équivalent de l’attribut « secure »).
Conclusion
L’utilisation de jeton JWT permet d’augmenter la sécurité des Web services via la mise en place de certains mécanismes cryptographiques. Cependant, la sécurité ne pourra être assurée qu’à condition de définir les différents éléments en fonction de leurs cas d’usage. Il sera ainsi nécessaire de :
- Utiliser des clés de taille suffisamment grande ;
- Adapter les algorithmes cryptographiques utilisés au contexte de l’application (SSO, Web service unique…) ;
- Mettre en place des mécanismes anti-rejeu (limiter la durée de vie des sessions…) ;
- Prévoir des mécanismes d’invalidation de jeton ;
- Conserver les clés de chiffrement de manière sécurisée.
Ressources :
- https://tools.ietf.org/html/rfc7519
- https://www.rfc-editor.org/rfc/rfc7515.txt
- https://www.rfc-editor.org/rfc/rfc7516.txt
- https://tools.ietf.org/html/rfc7518
- https://en.wikipedia.org/wiki/JSON_Web_Token
- https://jwt.io/
- https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/
- http://blog.prevoty.com/does-jwt-put-your-web-app-at-risk