Appuyez sur Entrée pour voir vos résultats ou Echap pour annuler.

8 secrets d’un bon jeton de sécurité JWT

À l’été 2015, je vais à Victoria en Colombie-Britanique pour un transfert de connaissances. Je découvre parmi une de leurs applications un frameworkAngularJS. Lorsque je remarque  la différence fondamentale entre une application monopage et une application plus traditionnelle, j’ai une illumination divine! Les APIs ont depuis explosé sur le web. Certaines grandes entreprises adoptent Angular et d’autres frameworks de SPA (Single Page Application ou application monopage) pour des applications à gros potentiel d’affaires. Aujourd’hui, je vous propose 8 secrets derrière les jetons de sécurités JSON ou JWT (Json Web Token).

* Il y a une checklist à la fin 😉

#1 – C’est un objet JSON

Bon… celui-là n’est pas vraiment un secret, mais JSON est le format de données utilisé. Cet objet JSON contient de l’information concernant l’utilisateur authentifié, telle que son adresse courriel. Il est créé au moment de l’authentification après avoir valider les identifiants de l’utilisateur, puis signé numériquement avec une clé secrète et encoder en Base64 avant d’être injecté dans les entêtes HTTP de la réponse.

 

#2 – Il remplace la session

Lorsque l’appelant est authentifié et a obtenu son jeton, il doit l’injecter dans chaque requête au serveur. Le principe étant basé sur le fait que la clé utilisée pour la signature du jeton est gardée secrète. Puisque seule l’application la connaît, le serveur peut être certain que la signature est la sienne et que le jeton n’a pas été altéré depuis. Ce jeton peut donc remplacer l’authentification lors d’appels subséquents à des services nécessitant une authentification.

#3 – Il contient le contexte de l’utilisateur

De l’information concernant l’utilisateur permettant de l’identifier de façon unique peut être mise dans le jeton de sécurité à condition que ce ne soit pas des données sensibles, comme des mots de passe ou des numéros de cartes bancaires. Le JWT est seulement encodé et peut facilement être décodé, mais pas altéré puisqu’il est signé. Il doit contenir de l’information qui permettra au serveur de retrouver l’appelant en BD (un GUID par exemple).

#4 – De base, JWT n’est pas sécuritaire

Un jeton peut facilement être volé. Après tout, il circule dans chacune des requêtes HTTP vers le serveur. La plupart des implémentations de base n’ont qu’une période de validité, ce qui fait que le jeton peut être réutilisé pour plusieurs requêtes pendant la durée de la session. Il ne peut donc pas être invalidé à distance ni localement sans mécanismes supplémentaires.

#5 – On peut améliorer la sécurité grâce à deux dates

À l’intérieur d’un JTW, il est possible de mettre deux dates de péremption. Une pour la durée globale de la session, l’autre pour s’assurer que le jeton est utilisé rapidement. Par exemple, on pourrait dire qu’un jeton peut vivre 8h et qu’il doit être rafraîchi par le serveur au moins une fois par 8-15 minutes. Ce dernier refusera tout jeton expiré et produira un nouveau jeton avec la date de péremption mise à jour.

#6 – Le point #5 crée une problématique

Chaque jeton créé par le serveur sera bon pour 15 minutes. Si on rafraîchit le jeton à chaque requête, on en créera des quantités astronomiques qui seront valide pour 15 minutes… Pour s’assurer que ils ne seront pas utilisés par quelqu’un d’autre, c’est possible de mettre l’adresse IP de l’appelant dans le JWT et de valider qu’elle correspond avec celle de l’appelant à chaque requête. Par contre, il faudrait encrypter le JWT pour éviter d’exposer l’information et que quelqu’un spoof son IP…

#7 – Sécurité ultime (j’exagère!)

Avant de me faire rentrer dedans par un puriste en sécurité (je te pardonne, je suis un puriste du code!), je ne dis pas que ce point va régler toutes les failles, mais vous auriez un très bon point de départ pour vos APIs publiques.

Considérant que nous avons un jeton signé avec une clé secrète qui doit être utilisé au moins toutes les 15 minutes, qui expirera après disons 8h, qui contient l’adresse IP de l’appelant et qui est encrypté, tout ce qu’il reste à faire c’est injecter un ID de session dans le JWT qui identifiera de façon unique la session globale de l’utilisateur. Un peu comme si on regroupait tous les jetons de session sous un même identifiant, souvent un GUID. De cette façon, il est possible de stoker les ID de sessions qui seraient invalidées si un jeton est utilisé de façon louche. Ainsi, les jetons peuvent être invalidés à distance si on a des doutes sur l’identité du propriétaire.

#8 – Level above 9000!

Notre jeton est pratiquement inviolable à ce moment, mais c’est toujours possible de le réutiliser. Par exemple, si votre navigateur est ouvert, vous pouvez voir le jeton passer dans les requêtes HTTP de la console développeurs. Il serait donc possible de tomber sur un cas où une personne pourrait garder le jeton en vie le temps trouver une façon d’entrer. Pour se protéger, il faut invalider tous les jetons qui ont été utilisés. Ceci se fait en injectant un ID de jeton (GUID) lors de la réponse et en validant si ce ID déjà été utilisé lors d’une requête. S’il a été utilisé, invalidez la session 🙂

TL;DR;

  1. Vos services n’acceptent que HTTPS (évidemment…)
  2. Un premier jeton est créé à l’authentification et contient l’IP de l’appelant, un identifiant unique pour l’utilisateur authentifié, un identifiant unique pour la session globale de l’utilisateur, une date d’expiration globale d’environ le temps nécessaire pour utiliser l’application, une date d’expiration pour le jeton actuel et un identifiant unique pour le jeton actuel.
  3. Le jeton est d’abord signé, puis encrypté et injecté dans la réponse.
  4. À chaque requête, les dates sont validées, le ID est de jeton est validé avec une base de données pour vérifier qu’il n’a pas été utilisé, le ID de session est validé pour vérifié qu’elle soit toujours active et l’adresse IP de l’appelant est validée pour s’assurer qu’elle n’a pas changé par rapport au JWT.
  5. Si une validation à #4 ne passe pas, invalider la session en BD.
  6. Si le jeton est OK, l’invalider et poursuivre normalement.
  7. Avant le retour, créer un nouveau jeton comme à l’étape 2.

IMPORTANT : Si vous appliquez tous ces points, il et primordial que l’appelant attende le nouveau jeton dans la réponse d’une requête avant d’en faire une autre. Les applications Angular, React ou autre framework SPA font des appels sans attendre la réponse. Il faut implémenter une file pour les requêtes pour régler ce problème.

Conclusion

Aujourd’hui, vous avez appris à créer des jetons de sécurité pour des APIs. L’important à retenir, c’est que ce n’est pas toutes les applications qui nécessitent ce niveau de sécurité. Un jeton valide pour 4 h avec un ID de session qu’on peut invalider lorsque la route de déconnexion est appelée peut « faire la job » pour la plupart des applications.

Est-ce que j’en ai oublié? Est-ce que je vais trop loin? À toi de me le dire dans les commentaires! J’ai bien hâte d’engager la discussion avec vous. Si vous avez aimé, prenez 2 secondes pour partager avec vos collègues et si vous avez adoré, payez-moi un drink!

Suivez-nous par courriel!

Saisissez votre adresse courriel pour vous abonner au blog d'Ezo et recevoir une notification de chaque nouvel article par email.