« JWT SPA symfony exercice pratique » : différence entre les versions
Aucun résumé des modifications |
Aucun résumé des modifications |
||
| Ligne 17 : | Ligne 17 : | ||
La promesse de JWT est simple: dans le frontend, vous effectuez une action de connexion qui retourne un JWT (JSON Web Token). Avec ce JWT, vous pouvez ensuite effectuer d'autres actions sur le serveur qui nécessitent une authentification en fournissant uniquement le JWT. Le délai d'authentification et d'expiration fait partie du JWT. | La promesse de JWT est simple: dans le frontend, vous effectuez une action de connexion qui retourne un JWT (JSON Web Token). Avec ce JWT, vous pouvez ensuite effectuer d'autres actions sur le serveur qui nécessitent une authentification en fournissant uniquement le JWT. Le délai d'authentification et d'expiration fait partie du JWT. | ||
== Cela présente quelques avantages : == | |||
*Vous n'êtes pas obligé d'envoyer un nom d'utilisateur et un mot de passe à chaque demande | *Vous n'êtes pas obligé d'envoyer un nom d'utilisateur et un mot de passe à chaque demande | ||
*Vous pouvez facilement évoluer vers plusieurs serveurs sur le backend sans avoir besoin d'un stockage de session central (mot-clé sans état) | *Vous pouvez facilement évoluer vers plusieurs serveurs sur le backend sans avoir besoin d'un stockage de session central (mot-clé sans état) | ||
== Mais aussi un inconvénient majeur : == | |||
Lorsque vous demandez et gérez le JWT via votre application Javascript, tous les autres Javascript exécutés sur la page peuvent lire le JWT et potentiellement l'envoyer à un attaquant. Cet attaquant est alors en mesure d'envoyer des requêtes au nom de l'utilisateur. | Lorsque vous demandez et gérez le JWT via votre application Javascript, tous les autres Javascript exécutés sur la page peuvent lire le JWT et potentiellement l'envoyer à un attaquant. Cet attaquant est alors en mesure d'envoyer des requêtes au nom de l'utilisateur. | ||
== Aggraver le problème == | |||
Habituellement, le JWT n'est pas utilisé comme une goutte en remplacement d'une session avec une longue date d'expiration, mais avec une très courte, quelque chose comme 15 minutes. Bien sûr, vous ne voulez pas que l'utilisateur soit déconnecté toutes les 15 minutes. Vous améliorez donc la configuration avec un jeton d'actualisation. À travers un intercepteur, chaque fois qu'une réponse interdite est renvoyée (car le JWT est expiré), un nouveau JWT est demandé. Cela se fait via le jeton d'actualisation qui a lui-même une date d'expiration beaucoup plus longue. Selon la configuration, la date d'expiration est même prolongée chaque fois qu'un nouveau JWT est demandé via le jeton d'actualisation. | Habituellement, le JWT n'est pas utilisé comme une goutte en remplacement d'une session avec une longue date d'expiration, mais avec une très courte, quelque chose comme 15 minutes. Bien sûr, vous ne voulez pas que l'utilisateur soit déconnecté toutes les 15 minutes. Vous améliorez donc la configuration avec un jeton d'actualisation. À travers un intercepteur, chaque fois qu'une réponse interdite est renvoyée (car le JWT est expiré), un nouveau JWT est demandé. Cela se fait via le jeton d'actualisation qui a lui-même une date d'expiration beaucoup plus longue. Selon la configuration, la date d'expiration est même prolongée chaque fois qu'un nouveau JWT est demandé via le jeton d'actualisation. | ||
| Ligne 32 : | Ligne 32 : | ||
Et ce jeton d'actualisation est également lisible par Javascript et donc ouvert à la saisie par un attaquant. Pour aggraver les choses, les jetons d'actualisation ne sont pas supprimés. Déjà. Ainsi, même si un nouveau jeton d'actualisation est généré via une nouvelle connexion valide de l'utilisateur, l'attaquant dispose toujours d'un jeton d'actualisation (potentiellement illimité) avec lequel travailler. | Et ce jeton d'actualisation est également lisible par Javascript et donc ouvert à la saisie par un attaquant. Pour aggraver les choses, les jetons d'actualisation ne sont pas supprimés. Déjà. Ainsi, même si un nouveau jeton d'actualisation est généré via une nouvelle connexion valide de l'utilisateur, l'attaquant dispose toujours d'un jeton d'actualisation (potentiellement illimité) avec lequel travailler. | ||
== Réponse: N'utilisez pas JWT ? == | |||
Malheureusement, c'est la réponse que j'ai trouvée le plus souvent dans les articles sur le sujet. | Malheureusement, c'est la réponse que j'ai trouvée le plus souvent dans les articles sur le sujet. | ||
| Ligne 38 : | Ligne 38 : | ||
Mais je n'aime pas vraiment cette réponse. Voyons donc quelles autres options nous avons. Et voyons quels sont les problèmes sous-jacents. Le seul problème que nous ne pourrons pas résoudre est que le JWT et le jeton d'actualisation sont lisibles par Javascript (pour des raisons évidentes). | Mais je n'aime pas vraiment cette réponse. Voyons donc quelles autres options nous avons. Et voyons quels sont les problèmes sous-jacents. Le seul problème que nous ne pourrons pas résoudre est que le JWT et le jeton d'actualisation sont lisibles par Javascript (pour des raisons évidentes). | ||
== Les problèmes que nous devons résoudre : == | |||
*Un JWT suffit pour authentifier une requête API | *Un JWT suffit pour authentifier une requête API | ||
| Ligne 50 : | Ligne 50 : | ||
*Différents jetons d'actualisation pour le même utilisateur sont valides | *Différents jetons d'actualisation pour le même utilisateur sont valides | ||
== La solution : == | |||
Comme le JWT est lié au jeton d'actualisation, le jeton d'actualisation est notre point de défaillance unique et il est logique d'y améliorer la sécurité. | Comme le JWT est lié au jeton d'actualisation, le jeton d'actualisation est notre point de défaillance unique et il est logique d'y améliorer la sécurité. | ||
Pour une sécurité maximale, nous n'autoriserons jamais deux sessions avec le même utilisateur à la fois. Ce qui signifie que si l'utilisateur est connecté sur un appareil puis se connecte sur un autre appareil, sa session sur le premier sera supprimée. | Pour une sécurité maximale, nous n'autoriserons jamais deux sessions avec le même utilisateur à la fois. Ce qui signifie que si l'utilisateur est connecté sur un appareil puis se connecte sur un autre appareil, sa session sur le premier sera supprimée. | ||
=== 1) Supprimer le jeton d'actualisation après utilisation === | |||
Depuis la version 0.7.0, le bundle de jetons d'actualisation contient une nouvelle option single_use qui supprime un jeton d'actualisation après utilisation et en émet un nouveau. Cela signifie qu'une fois qu'un attaquant vole un jeton d'actualisation, il n'a que jusqu'à la prochaine utilisation de l'utilisateur pour utiliser le jeton d'actualisation. Après c'est déjà inutile. Alors activons cela dans le fichier gesdinet_jwt_refresh_token.yml. | Depuis la version 0.7.0, le bundle de jetons d'actualisation contient une nouvelle option single_use qui supprime un jeton d'actualisation après utilisation et en émet un nouveau. Cela signifie qu'une fois qu'un attaquant vole un jeton d'actualisation, il n'a que jusqu'à la prochaine utilisation de l'utilisateur pour utiliser le jeton d'actualisation. Après c'est déjà inutile. Alors activons cela dans le fichier gesdinet_jwt_refresh_token.yml. | ||
| Ligne 67 : | Ligne 67 : | ||
Cela rend évidemment le paramètre ttl_update: true inutile. | Cela rend évidemment le paramètre ttl_update: true inutile. | ||
=== 2) Supprimer les jetons d'actualisation lors de la déconnexion === | |||
Après la déconnexion, il n'est pas nécessaire de disposer d'un jeton d'actualisation, alors assurez-vous qu'ils sont supprimés. | Après la déconnexion, il n'est pas nécessaire de disposer d'un jeton d'actualisation, alors assurez-vous qu'ils sont supprimés. | ||
| Ligne 122 : | Ligne 122 : | ||
</pre> | </pre> | ||
=== 3) Ajouter un cookie à l'authentification JWT === | |||
Tant que le JWT est suffisant pour l'authentification, c'est toujours notre point de défaillance unique. Et même si notre jeton d'actualisation a maintenant moins de valeur, il est toujours suffisant pour générer un nouveau JWT. Nous avons donc besoin d'un composant supplémentaire qui n'est pas lisible via Javascript. Et c'est exactement ce qu'est un cookie http uniquement. Il ne peut être défini et lu que par le serveur et sera automatiquement envoyé par le navigateur à chaque demande. | Tant que le JWT est suffisant pour l'authentification, c'est toujours notre point de défaillance unique. Et même si notre jeton d'actualisation a maintenant moins de valeur, il est toujours suffisant pour générer un nouveau JWT. Nous avons donc besoin d'un composant supplémentaire qui n'est pas lisible via Javascript. Et c'est exactement ce qu'est un cookie http uniquement. Il ne peut être défini et lu que par le serveur et sera automatiquement envoyé par le navigateur à chaque demande. | ||