JSON Web Token (JWT) : Le guide complet

PrimFX Boris ('PrimFX') Le 19 juin 2020

Principes de sécurité des JWT Tokens

Les JWT ne sont ni encryptés, ni hachés !

Lorsqu’ils sont transmis entre client et serveur, les JWT sont encodés en Base64. Cela signifie qu’ils sont convertis dans un format “facile à transmettre”. Cet encodage n’offre cependant aucune sécurité (contrairement à un encryptage ou un hachage).

Pour autant, cela ne veut pas dire que les JWT ne sont pas sécurisés, et heureusement d’ailleurs ! En réalité, la principale sécurité d’un JWT repose sur sa signature que seul un serveur connaissant la clé secrète pourra vérifier.

 

Pour être tout à fait précis, les JWT peuvent être encryptés à la place d’être signés, mais cette pratique est très peu répandue. On verra plus loin que c’est en fait généralement HTTPS qui encryptera la communication client/serveur et, par conséquent, le JWT qui est à l’intérieur ! 

Les JWT sont des jetons signés

Comme expliqué précédemment, la troisième partie d’un JWT est la Signature, qui est générée par un algorithme de hachage à partir de l’intégralité des informations du token (i.e. à partir de son Header et de son Payload) et d’une clé secrète connue uniquement du serveur. Cette fonction de hachage va ainsi générer un hash (chaîne de caractères) stockée dans le JWT lui-même : c’est sa Signature.

 

La clé secrète est simplement une chaîne de caractère complexe (contenant des lettres, chiffres, caractères spéciaux, etc.) que vous pouvez créer. Vous pouvez également passer par des générateurs aléatoires de clés comme randomkeygen.com par exemple.

A chaque fois que le serveur recevra une requête contenant un JWT, il re-générera la Signature à partir des Header & Payload du JWT et de sa clé secrète. Si le hash obtenu est identique à la signature du jeton : le JWT est bien valide ! Ainsi, seul le serveur ayant connaissance de cette clé secrète sera capable de déterminer si le jeton communiqué par un client est bien valide ou non 😃

Par exemple, reprenons notre JWT signé par la clé « CLE-SECRETE » :

Exemple JWT Signature

 

On voit bien que la signature est vérifiée (en-bas à gauche de l’image).

 

Vous pouvez essayer de reproduire vous-même les manipulations suivantes sur le débugger de jwt.io avec les informations suivantes :

  • Jeton (encodé en Base64) : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoiMTIzNCIsInVzZXJuYW1lIjoiUHJpbUZYIiwiZW1haWwiOiJwcmltZnhAZXhhbXBsZS5jb20ifSwiaWF0IjoxNTkyNDIzNTAxfQ.OVj3A02xao5gyPgZY33b8QX7KtnkbmoFd6GbBnkwNVE
  • Clé secrète : CLE-SECRETE

A présent, voici ce qu’il se passe si je modifie la clé secrète du serveur par « CLE-INVALIDE » :

Exemple JWT clé invalideLa signature n’est plus valide ! De même, on observe un comportement similaire si la signature est la bonne mais que l’on modifie la partie Payload de notre JWT encodé en Base64 (j’ai ici modifié le JWT de façon à avoir l’id utilisateur “5678” au lieu de “1234”) :

Exemple JWT modifié - Signature invalide

 

🔐 Puisque la signature est générée à partir de toutes les informations du JWT (Header et Payload), le moindre changement dans les informations du JWT (ou dans la clé secrète) entraîneront l’invalidité de la signature ! Plutôt classe n’est-ce pas ? 🤩

Ce mécanisme de signature rend d'ailleurs impossible pour n’importe quel utilisateur mal intentionné d’aller modifier manuellement son JWT pour modifier ses droits.

Les Claims : des paramètres de sécurité

Comme indiqué précédemment, le Payload de nos JWT peut non seulement contenir des informations personnalisées, mais il peut également contenir un certain nombre de paramètres prédéfinis (i.e. spécifiés dans la documentation RFC 7519 de JWT) appelés Claims. Ces paramètres sont essentiellement liés à la sécurité du jeton.

 

Parmi tous les paramètres prédéfinis du Payload, les plus communément utilisés (et recommandés pour des raisons de sécurité) sont iat et surtout exp afin de limiter temporellement l’utilisation d’un jeton. Tous les autres paramètres sont à utiliser selon vos besoins 🤓

Expiration time

 

L’expiration d’un jeton est certainement le paramètre de sécurité le plus important sur lequel vous avez la main.

Les JWTs peuvent avoir une durée de vie, et je vous recommande fortement d’en préciser une par précaution / pour des raisons de sécurité avec le paramètre exp du JWT.

Puisque la date d’expiration d’un token est indiquée dans son Payload, celle-ci doit être déterminée lors de la création du JWT, côté serveur. On fixe généralement la durée d’un token à quelques dizaines de minutes au maximum (très souvent 15 minutes). Il reste toutefois possible de fixer cette durée à plusieurs heures : tout dépend du niveau de sécurité souhaité.

Formellement, cette date est indiquée dans le paramètre exp par un timestamp : le nombre de secondes depuis Epoch (i.e. le 1er Janvier 1970).

 

La plupart des langages de programmation permettent de récupérer facilement cette valeur temporelle 😀

Issued at

Dans la même idée que exp, le paramètre "Issued at" (iat) permet de savoir précisément quand le jeton a été généré. Il est également représenté par le nombre de secondes depuis Epoch.

Not before

Le paramètre "Not before" (nbf) indique à partir de quelle date le JWT commencera à être valide. Cela permet de ne pas rendre le jeton valide dès sa génération mais seulement à partir d’une date précise, donnée elle aussi en secondes depuis Epoch comme pour exp et iat.

Issuer

L’"Issuer" (iss) permet de savoir qui a généré ce jeton. On peut par exemple y placer le nom ou l'URL du serveur, du service ou de l’application qui a généré le JWT.

Subject

Le "Subject" (sub) permet de savoir ou pour qui a été généré le jeton. On peut par exemple y placer l’identifiant de l’utilisateur connecté.

Audience

Le paramètre aud indique l'audience à laquelle est destiné ce JWT. Cela permet, par exemple, de distinguer une audience « dev » (développement) d’une audience « prod » (production) lors du développement d’une application. On peut également s’en servir pour indiquer à quel « type » d’audience le jeton appartient : “application-mobile”, “application-web”, “client-logiciel”, etc.

 

Cela peut permettre à votre serveur d’adopter un comportement différent en fonction de l’audience du jeton.

JWT ID

Le JWT ID (jti) permet simplement d’attribuer un identifiant unique à un jeton.

En savoir plus

Pour plus d’informations sur tous ces paramètres, je vous invite à faire un tour sur la spécification de JWT.

Et si un JWT se fait dérober ?

Hypothèse : un client envoie son JWT dans chaque requête à un serveur. Malheureusement, un hacker se trouve sur ce réseau et intercepte une requête HTTP contenant le JWT. Pourra-t-il alors récupérer le JWT et se faire passer pour un utilisateur légitime auprès de notre serveur ?

En théorie, oui !

Petit rappel : un JWT n’est pas encrypté. Il est simplement encodé en Base64. Cela signifie que si une tierce personne parvient à dérober le JWT d’un utilisateur, il pourra non seulement se faire passer pour cet utilisateur auprès de notre serveur mais il pourra aussi voir toutes les informations que ce JWT contient.

 

Mais alors, les JWT ne sont pas si sécurisés ?

Pas d’inquiétude, les JWT sont bien sécurisés. En fait, un JWT n’est pas un protocole d’authentification. C’est simplement une spécification du format de jeton sécurisé. Cela signifie qu’il faut transmettre les jetons JWT à travers un protocole d’échange sécurisé ! Et lorsqu’on parle d’architecture client/serveur, d’APIs et de requêtes HTTP, c’est forcément à HTTPS que l’on fait référence.

Ainsi, à partir du moment où votre jeton sera transmis par un réseau sécurisé (i.e. encrypté), le risque qu’un JWT soit dérobé est infime.

 

🔐 Pour que votre utilisation de JWT soit sécurisé, il faudra donc toujours réaliser vos communications client/serveur de façon encryptée, c’est-à-dire en HTTPS (à l’aide d’un certificat SSL).

 

💡 J’ai répété plusieurs fois que les JWT ne sont pas encryptés. En réalité, c’est toujours HTTPS (le protocole de communication à travers lequel passe notre JWT) qui se chargera d’encrypter toutes les communications client/serveur et, par conséquent, d’encrypter notre JWT qui n’était jusqu’à cette étape qu’encodé en Base64.

Et si un hacker trouve ma clé secrète ?

La clé secrète ne s’appelle pas “clé secrète” pour rien. Si celle-ci était découverte, vous risquez d’importants problèmes de sécurité. Mais pas d’inquiétude, tout n’est pas encore perdu : vous n’avez qu’à modifier votre clé secrète et votre système de génération & vérification de JWT sera à nouveau fonctionnel ! La seule conséquence de la remise à zéro de la clé secrète est que tous les JWT précédemment générés seront considérés comme invalides par votre serveur. Autrement dit, tous vos utilisateurs connectés au moment du changement de la clé secrète devront se reconnecter : c’est un (très petit) mal pour un bien.

 

Pensez toutefois à investiguer “comment” a été récupérée votre clé secrète : cela évitera de vous faire voler votre nouvelle clé ! 


A propos de l'auteur

PrimFX
Boris ('PrimFX')

Je m'appelle Boris, j'ai 22 ans et je suis passionné d'informatique. Suite à mes études (Licence Informatique puis MSc Computer Science au Trinity College Dublin), je gère l'entreprise Single Quote co-fondée en 2019 et je profite de mon temps libre pour partager ma passion à travers des vidéos & articles 😃