Quelques bonnes pratiques lorsqu'on utilise des JWT

JWT est une technologie bien connue des développeurs aujourd’hui. On l’utilise très souvent comme clé d’API, comme access token lorsqu’on fait du OAuth2 ou encore comme id token dans le cadre d’OpenID Connect. En clair, ça sert à faire de l’authentification et/ou de l’autorisation. Et comme tout ce qui touche à ce genre chose, il est primordiale d’y apporter une attention toute particulière ! C’est un angle d’attaque intéressant pour un attaquant non ?

Grosso modo, la plupart du temps, un JWT c’est du JSON, encodé en base64, signé avec un HashMAC (enfin, dans le cas d’un jeton signé). On peut être tenté de penser que c’est très simple, et que donc “on a tout compris très vite”. Mais en fait, pour se prémunir des attaques et éviter toute faille de sécurité, il est très important d’aller un peu plus loin que ça. Je vous donne ici quelques conseils pour une utilisation plus “safe”.

Une bonne situation

A propos du secret

Que vous fassiez de la signature, du chiffrement, du symétrique, de l’asymétrique (etc), il est indispensable de gérer la/les clés que vous utilisez avec la plus grande sécurité. Il vous faut une vraie politique de gestion, de renouvellement, etc. Un secret (de production) doit être conservé dans un Vault (ou autre techno) et ne doit jamais être communiqué à un développeur ou toute autre personne qui n’aurait pas le droit d’y accéder (c’est à dire pas grand monde). Si les secrets fuitent, tous les conseils qui suivent ne servent pas à grand chose… De plus, avoir des clés robustes signifient souvent avoir des clés de grandes tailles. Oubliez azertyui01 ou autre, blindez vous en choisissant une clé bien plus longue.

Préférez l’asymétrique

Il est souvent plus intéressant d’utiliser des clés asymétriques plutôt que symétriques. Pourquoi ? Si vous communiquez une clé symétrique à une tierce partie, ce secret devient de fait, plus fragile, plus difficile à protéger et à plus de chance de fuiter. Même si l’utilisation d’une clé symétrique peut se faire, par exemple si vous êtes à la fois l’émetteur et le récepteur du token, choisir des clés asymétriques ne peut vous apporter que des bénéfices. Concernant les performances (les opérations avec clés symétriques sont plus rapides qu’avec des clés asymétriques), ceci est négligeable sur la quantité de données stockées dans un JWT.

N’acceptez pas tout

L’entête (ou header) d’un JWT contient un certain nombre d’informations sur les opérations cryptographiques qui ont été utilisées pour construire le-dit jeton. On peut trouver notamment, l’algorithme utilisé ou des URLs pointant sur une clé publique par exemple. Ne faites pas confiance aveuglément à ça ! Validez toujours ce que vous trouvez dans l’entête en utilisant une liste d’autorisation que ce soit pour les algo ou pour les domaines des URLs. Bien souvent, on peut hard coder une liste d’algo utilisés et de domaines autorisés.

Validez les claims

Dans la spécifications JWT, il y a une partie du contenu (ou payload) qui est réservée. Celle-ci s’appelle les “registered claims”. Ils contiennent des informations sur le jeton : qui l’a émit ? Pour qui a-t-il été émis ? A partir de quand est-il valide ? Quand sera-t-il expiré ? Etc… Vous devez valider toutes ces informations pour notamment vous prémunir des attaques par substitutions. En clair, même si le jeton est valide (au sens au la signature atteste que le jeton n’a pas été modifié) mais qu’un des claims n’est pas ce que vous attendez, vous devez le refuser.

Ne réinventez pas la roue

Il est plutôt rapide d’écrire quelques lignes de code permettant de générer et/ou de valider un JWT donc c’est tentant de le faire soit même. Mais c’est également rapide de laisser des failles dans votre implémentation ! D’après moi, le meilleur choix de loin, est de se reposer sur une implémentation solide, reconnue, écrite et auditée par des développeurs qui connaissent très bien le sujet (mieux que vous et moi probablement 😉).

Collez à la spécification

Ne cherchez pas à tordre cette technologie dans tous les sens pour mieux correspondre à vos besoins. JWT est robuste pour peu que vous suiviez scrupuleusement ses spécifications. Par exemple, n’utilisez pas d’algorithme non prévu dans la spécification JWA. Cela pourrait introduire des failles inattendues. Par exemple, certains algorithmes de chiffrement n’assurent pas l’intégrité des données. Il serait donc possible pour un attaquant de modifier le contenu du JWT sans même avoir besoin de casser le chiffrement.

Attention au contenu

Un token signé n’assure pas la confidentialité des données : un simple “base64 decode” permet de lire son contenu. Il est donc très important de ne pas mettre d’information sensible (🚨 RGPD) et encore moins de mot de passe. Attention également aux traces qu’un JWT peut laisser au niveau du monitoring. On a vite fait de logger un JWT et donc potentiellement, d’exposer et de conserver des informations sur nos utilisateurs en production.

Ne vous battez pas pour les révoquer

Un des gros avantages des JWT c’est qu’ils portent leur moyen de validation et donc il permet de se passer d’une requête vers un serveur d’autorisation pour déterminer si le token est valide ou non. Pour la scalabilité c’est plutôt intéressant ! Alors si on remplace ce genre d’appel par un appel pour vérifier si le token est révoqué on perd un peu l’intérêt. Alors oui ce n’est pas tout à fait la même chose bien sur ! Il y a des méthodes plus légères pour gérer la révocation des tokens que pour les autoriser (avec tout ce que ça implique). Mais quand même 😉. Peut-être vous suffit-il de mettre une durée de vie plus courte à vos JWT ?

Conclusion

Il est nécessaire de prêter attention à l’utilisation que vous faites de cette techno. Comme souvent, ce qui semble simple de premiers abords se révèle plein de subtilités, d’autant plus quand ça touche à la sécurité ! Ne faites aucune confiance à ce que vous trouverez dans l’entête du jeton, validez tout ce qui doit être validé et reposez vous sur le travail de gens compétents sur le sujet et vous vous éviterez pas mal de problèmes !