Jetons JWT et sécurité – Principes et cas d’utilisation

Sous PHP, la méthode principale pour gérer des sessions utilisateurs est l’utilisation de cookies de sessions appelés par défaut « PHPSESSID ». Lorsqu’un utilisateur se connecte sur une application, celle-ci demande la génération d’un identifiant de session au programme PHP présent sur le serveur Web. Celui-ci s’exécute et créé un code unique qu’il va stocker dans sa mémoire vive puis renvoyer au client grâce au header « Set-Cookie ». Les cookies étant conçus pour être automatiquement renvoyés dans chaque requête du navigateur vers le serveur, l’identifiant de session sera transmis systématiquement. Cette solution permet de gérer les cas classiques de connexion/déconnexion d’un utilisateur.

Cependant, ce type de fonctionnement ne permet pas de partager facilement un même compte pour s’authentifier sur plusieurs plateformes distinctes et le serveur a l’obligation de stocker l’état et les informations des sessions dans sa mémoire vive.

Les jetons JWT quant à eux sont “stateless”, cela signifie que les informations des sessions ouvertes ne sont pas stockées côté serveur. Outre le gain de mémoire que cela procure, les jetons JWT peuvent être utiles si vous souhaitez utiliser les mêmes comptes utilisateurs pour plusieurs applications. Il suffira que les applications utilisent la même clé privée pour signer et vérifier les jetons JWT. Les utilisateurs pourront ainsi s’authentifier une fois sur l’application hébergeant les comptes utilisateurs, puis ils pourront se connecter aux autres applications, celles-ci n’ayant qu’à vérifier la validité des jetons.

Description

Les « JSON Web Token » ou JWT sont des jetons générés par un serveur lors de l’authentification d’un utilisateur sur une application Web, et qui sont ensuite transmis au client.

Ils seront renvoyés avec chaque requête HTTP au serveur, ce qui lui permettra d’identifier l’utilisateur.

Pour ce faire, les informations contenues dans le jeton sont signées à l’aide d’une clé privée détenue par le serveur. Quand il recevra à nouveau le jeton, le serveur n’aura qu’à comparer la signature envoyée par le client et celle qu’il aura générée avec sa propre clé privée et à comparer les résultats. Si les signatures sont identiques, le jeton est valide.

Structure d’un jeton JWT

Si la norme RFC 7519 est bien respectée, un jeton JWT se compose de cette façon :
Une partie “Header”, contenant l’algorithme utilisé pour la signature ainsi que le type de jeton (dans notre cas toujours “JWT »), en JSON encodé en Base64
Une partie “Payload” contenant les informations du jeton, comme par exemple le nom de l’utilisateur, la date d’émission du jeton ou sa date d’expiration le tout en JSON encodé en Base64
Une partie “Signature”, qui correspond à la concaténation des parties “Header” et “Payload” chiffrée avec la clé privée.

Voici un exemple de jeton JWT :

Partie “Header” :
{
“alg“:“HS256“,
“typ“:“JWT“
}

Partie“Payload“ :
{
“iat“: 1480929282,
“exp“: 1480932868,
“name“:“Username“
}

Dans cette deuxième partie, il est possible d’inscrire beaucoup d’informations, comme le nom d’utilisateur ou ses droits par exemple. Cependant le standard spécifie certains mots clés comme iat (issued at : date et heure d’émission du jeton sous forme de timestamp) ou exp (expiration du jeton).

La signature est donc générée à partir de la concaténation des parties “Header” et “Payload” encodées en Base64, soit, par exemple, avec la clé privée “L2VE5VpgChrVPmgh1hgL” :

Signature = HMACSHA256(
base64UrlEncode({“alg“:“HS256“,“typ“:“JWT“}) +“.“ +
base64UrlEncode({“iat“: 1480929282,“exp“: 1480932868,“name“:“Username“}),
L2VE5VpgChrVPmgh1hgL
)

Ce qui nous donne le jeton JWT suivant :

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0ODA5MjkyODIsImV4cCI6MTQ4MDkzMjg2OCwibmFtZSI6IlVzZXJuYW1lIn0.gZeuWNbjO8kyEX92AjgX5oLy5qhu6YWTPr6vtYELZQ4
Les couleurs correspondent aux parties suivantes :
Header
Payload
Signature

Quelle signature utiliser

Le standard JWT laisse la possibilité au développeur de ne pas utiliser d’algorithme pour signer le token. Il est bien entendu totalement déconseillé d’utiliser cette spécification.
Il est par contre possible de choisir quel algorithme nous allons utiliser. Il est donc possible d’utiliser du HMAC SHA256 comme dans les exemples de cet article (ce qui est recommandé) ou d’utiliser un système de chiffrement asymétrique (avec une clé privée détenue par le serveur pour vérifier le chiffrement et une clé publique pour signer le token). Le problème de cette méthode et qu’elle permet à n’importe qui de signer un token car il est seulement nécessaire de posséder la clé publique. Pour plus d’informations, vous pouvez consulter l’article suivant décrivant en détail cette vulnérabilité :
https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/

keys copy jwt

Cas d’utilisation

Les jetons JWT peuvent être utilisés principalement de deux manières côté client : stocké dans un cookie ou dans le sessionStorage (ou le localStorage) du navigateur. Chacune de ces solutions présente des avantages et des inconvénients en termes de sécurité et de fonctionnalité.

Jeton stocké dans le sessionStorage ou dans le localStorage du navigateur

Si le jeton est stocké de cette manière, il devra être inclus systématiquement dans les requêtes envoyées au serveur, par exemple via le header « Authorization: Bearer <jeton> ». Cette solution est donc idéale pour des applications principalement Frontend en Javascript et où celui-ci fonctionne principalement avec des requêtes AJAX. Le développeur devra donc ajouter le jeton dans chaque requête. Cette solution a l’avantage de protéger nativement l’application contre les attaques de type CSRF, le jeton n’étant absolument pas prévisible ou récupérable par l’attaquant, ceci n’étant valable que si toutes les requêtes sensibles nécessitent le jeton JWT. Cependant, comme le jeton doit être disponible pour l’application javascript, il sera exposé en cas de faille XSS et pourra être récupéré.

Jeton stocké dans des cookies

Lorsqu’un jeton JWT est stocké dans un cookie sur le navigateur de l’utilisateur, il est possible de lui attribuer le flag “HttpOnly” (et “Secure”) afin de le protéger contre une éventuelle récupération via une attaque de type XSS. Par contre, là ou un jeton stocké dans le sessionStorage ou le localStorage du navigateur pouvait protéger contre les attaques de type CSRF, les cookies ne le permettent pas. En effet cela n’est plus possible avec un jeton stocké dans un cookie, celui-ci étant automatiquement envoyé par le navigateur avec chaque requête. Bien qu’elle reste « stateless », cette solution à également le désavantage de ne plus permettre une authentification sur une autre application d’un autre domaine car les cookies, par mesure de sécurité, ne peuvent être renvoyés que sur le domaine du serveur qui les a créés.

Inconvénients des jetons JWT

Outre les avantages et les inconvénients évoqués plus haut, le standard JWT présente quelques problèmes :

  • Si un compte utilisateur doit être bloqué, il faudra attendre que le jeton expire pour que le blocage soit effectif
  • Si un utilisateur veut changer son mot de passe (par exemple en cas de piratage de son compte) et si l’authentification a été effectuée juste avant, le jeton généré grâce à l’ancien mot de passe sera toujours valide jusqu’à expiration
  • Pas de refresh de jeton possible dans l’implémentation standard, ce qui veux dire que lorsque le jeton expire, l’utilisateur doit s’authentifier à nouveau
  • Il n’est pas possible de détruire un jeton tout en respectant la notion de “stateless” des jetons JWT, car même si on supprime un jeton du navigateur, celui-ci est toujours valide jusqu’à expiration, donc pas de réelle déconnexion possible

Pour solutionner en partie les problèmes listés ci-dessus, certaines librairies mettent en place des mécanismes supplémentaires permettant entre autres de rafraîchir des sessions ou de forcer la ré-authentification d’un utilisateur si nécessaire.

Conclusion

Les jetons JWT sont de plus en plus présents sur les applications principalement Frontend et là où il est nécessaire d’utiliser un même compte utilisateur sur plusieurs plateformes. Du point de vue de la sécurité, les jetons JWT sont relativement bien conçus mais présentent quelques petits inconvénients qu’il faut bien comprendre et appréhender avant de les implémenter dans une application. De plus, il ne faut pas perdre de vue que la sécurité entière du système d’authentification de l’application repose sur l’algorithme utilisé ainsi que sur la clé privée qui servent à la signature des jetons. Il faut donc s’assurer de choisir un algorithme solide et une clé très forte ainsi que garder cette dernière secrète.

Ressources

Pour aller plus loin, vous pouvez consulter les liens ci-dessous :
RFC 7519 des jetons JWT : https://tools.ietf.org/html/rfc7519
Site présentant les jetons JWT et listant les librairies disponibles pour la plupart des langages : https://jwt.io/

Auteur : Romain GARCIA