David Hockley

Sessions vs JWT : C’est quoi un JWT ?

C’est quoi un JWT? C’est un “JSON Web Token”. Certes, et maintenant qu’on a dit ça, ça nous avance pas des masses.

Et bien ensemble nous allons explorer :

  • ce à quoi sert un JWT
  • et comment un JWT est structuré et construit
  • et en prime nous allons voir ce que signifie de concevoir un serveur “stateless”,

Mais d’abord… à quoi sert un JWT ?

A quoi sert un JWT ?

Et bien pour le comprendre il faut comprendre le problème qu’il essaie de résoudre. Imaginez que vous êtes un serveur. Pas un serveur dans un restaurant, un serveur dans un data center.

Vous êtes un serveur, donc. ET vous recevez un message qui dit « montre moi la page d’accueil », ou pour parler HTTP, « GET /index »

Vous générez la page d’accueil, et vous la renvoyez. Jusque là tout va bien.

Maintenant imaginez que vous voulez afficher le prénom de la personne qui a demandé la page, s’il s’est connecté.

Ou imaginez que la personne demande une page genre /secret, qui est réservée aux employés, ou ceux qui ont acheté le droit d’avoir cette page, ou que sais-je.

Comment est-ce que vous gérer ça, du point de vue du serveur ?

Pour dire les choses différemment : comment, quand le serveur reçoit un message, est-ce que le serveur sait qui lui a demandé ?

Alors vous allez me dire : à un moment, l’utilisateur s’est connecté. Il a rentré un email et un mot de passe, donc on sait que c’est lui.

Oui, c’est vrai: le serveur reçoit un message sur /login avec un email et un mot de passe, il va vérifier en base de données que l’email et le mot de passe correspondent bien, il va renvoyer une erreur si ce n’est pas le cas.

Mais imaginons que ce message sur /login date d’il y a 10 minutes.

Imaginons qu’il y a des centaines de gens, chacun avec des droits différents, qui se connectent aussi.

Comment le serveur sait-il que tel message vient de tel utilisateur ?

Alors c’est ici qu’interviennent les entêtes (ou « header ») du message.

On peut y mettre de l’information.

On pourrait imaginer y mettre l’email de l’utilisateur pour l’identifier, mais ça suffirait pas, n’importe qui peut envoyer un message en mettant un email dedans.

On pourrait y rajouter le mot de passe, mais ça impliquerait deux choses :

  • Premièrement, de revérifier la base de données à chaque appel, ce qui est couteux
  • et deuxièmement, de faire transiter le mot de passe à chaque fois dans chaque message, ce qui est risqué.

Clairement ce n’est pas idéal.

La solution qui a longtemps été utilisée, c’est de créer une sorte de mot de passe temporaire, un secret, au moment où l’utilisateur se connecte.

Ensuite, le serveur garde en mémoire le lien entre tel secret et tel utilisateur.

Puis le navigateur envoie, dans les entêtes, ce secret à chaque appel, et le serveur, quand il reçoit le message, vérifie en mémoire si le secret correspond bien à un utilisateur.

Ce secret, c’est ce qu’on appelle l’identifiant de « session ».

Tout ça marche plutôt bien voire même très bien quand on a un seul serveur.

Mais imaginez que vous gérez une application avec des centaines de serveurs. ET devant ces serveurs, il y a un répartiteur de charge, qui envoie les messages vers les serveurs qui sont les moins utilisés.

Du coup quand un message arrive, il y a peu de chances que ce soit sur le même serveur que celui qui a servi au login.On ne peut donc plus compter sur le fait que le serveur garde les secrets en mémoire. On peut créer une base de donnée centrale, éventuellement en mémoire.

Mais ce serait beaucoup mieux si on pouvait rendre nos serveurs “stateless”. C’est à dire, en gros, capable de traiter les demandes sans devoir garder un état en mémoire entre les requêtes.

Ce qui permet une montée en charge beaucoup plus facile, puisqu’il y a pas besoin de communiquer un état partagé entre serveurs.

Et c’est là qu’intervient le JWT.

Comment marche un JWT ?

Le JWT, ou JavaScript Web Token, déplace le problème. Ce n’est plus le serveur qui gère l’état, c’est ce token qui le contient. Comment ?

Et bien pour ça regardons comment est construit un JWT. Un JWT ça ressemble à ce charabia de caractères :

eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiSm9lIENvZGVyIn0.5dlp7GmziL2QS06sZgK4mtaqv0_xX4oFUuTDh1zHK4U

Comme vous pouvez peut-être voir, il y a trois blocs de texte séparés par des points.

Ces blocs sont les trois parties des JWT, à savoir :

  • l’entête
  • le corps
  • la signature

Chacune des parties est encodée en Base64, ce qui n’a pas pour but d’apporter une sécurité cryptographique, mais juste de permettre de transiter paisiblement le token via un message HTTP.

Voyons ces trois parties:

L’entête précise l’algorithme utilisé pour la signature, avec le champ “alg” comme “algorithme.”

{"alg": "HS256" }

Ici, dans cet exemple, le HS256 indique que le token est sensé être signé avec une clef en SHA 256.

La deuxième partie, le corps, contient les claims, ou en français les “revendications”. Ici aussi il s’agit d’un document en JSON.

C’est par exemple ici qu’on indiquerait que le token identifie tel utilisateur, avec tel id et tels droits.

On peut (et, si on veut être sérieux, on doit) préciser les dates de création et d’expiration du token.

Pour ça la norme précise le nom des champs, par exemple le champ “exp” précise la timestamp d’expiration.

(Une timestamp c’est une date précisée en nombre de secondes depuis EPOCH, c’est à dire depuis le 1er Janvier 1970).

Donc ça peut ressembler à quelque chose comme ça :

{
  "id": 42,
  "name": "Kodaps",
  "role": "admin", 
  "exp": 1647807974
}

C’est donc cette partie ci qui stocke l’état et qui permet éventuellement au serveur de fonctionner de manière stateless.

Vient enfin la signature cryptographique. C’est elle qui permet de valider le token. L’idée c’est de signer le token avec un secret que seul le server ne connaisse. Comme ça si la signature correspond bien à ce secret, le serveur sait qu’il peut faire confiance à ce token.

Le serveur n’a besoin que de valider que le token tient la route, que c’est bien un token qu’il a lui même crée. Sans avoir besoin de consulter une base de données.

Le premier avantage du JWT, donc, c’est qu’il permet plus facilement que les serveur soient “stateless”, sans état.

Le deuxième avantage, c’est qu’il peut être utilisé dans le contexte d’applications mobiles, où la notion de cookie ou de session a moins de sens. Ou encore dans le contexte de la communication avec une API, ou de meme, la session a moins de sens.

Un troisième avantage, en utilisant une paire de clef asymétrique, un token signé avec une clef privée peut être validée avec une clef publique. Donc ça signifie qu’un organisme central, qu’on estime digne de confiance, peut émettre une sorte de certificat vérifiable, en disant : cet utilisateur possède bien tel caractéristique.

Et vous pouvez vérifiez avec la clef publique que j’ai publiée que son token JWT est valide. Et pour le coup, ce n’est plus seulement le serveur qui peut valider le certificat, mais n’importe quelle entité qui a accès à la clef publique.

Pour résumer.

Un JWT est un objet avec une signature cryptographique, qui encapsule un état exprimé sous la forme de “claims”. En fonction de l’algorithme utilisé, le token peut être validé par le serveur lui-même, ce qui est utile notamment dans le cas d’application, ou d’appels API, ou de serveur stateless.

Et le token peut être validé de manière plus large, ce qui permet de mettre en place des mécanismes de tiers de confiance.

Social
Made by kodaps · All rights reserved.
© 2023