J'implémente des JWT dans mon application et j'aimerais les rendre aussi sécurisés que possible. Je vais exposer tout ce que je prévois, j'apprécierais grandement toutes les suggestions quant à la sécurité de cette mise en œuvre. Ceci est mon site, j'ai un accès complet à tous les aspects de celui-ci, à la fois front-end et back-end.
Ce sera un SPA, utilisant une API pour accéder au back-end. J'utilise des JWT pour enregistrer les appels DB avec chaque hit d'API.
Les JWT sont stockés dans un access_token
biscuit. Ils sont d'abord signés puis chiffrés à l'aide de jose-jwt . L'algorithme de signature est HS256, le cryptage est DIR. Ils incluent l'ID de l'utilisateur, une revendication d'expiration exp
et plusieurs autres revendications personnalisées. Les JWT expirent dans 30 minutes et le cookie JWT expire dans 7 jours.
(version courte):
exp
revendication définie à 30 minutes dans le futurLes JWT incluent une revendication cst
qui stocke un jeton CSRF généré de manière aléatoire. Le jeton CSRF est envoyé dans le corps de la réponse lors de la connexion et lorsqu'un nouveau JWT est émis. Le jeton CSRF est stocké dans le stockage local du navigateur. Il est envoyé avec chaque demande et validé par rapport à la valeur dans le JWT.
(version courte):
Comme les JWT expirent dans 30 minutes, il est nécessaire de les rafraîchir. Le JWT inclut une revendication rfs
qui stocke un jeton d'actualisation aléatoire. Ce jeton d'actualisation est également stocké dans la base de données (une table distincte des utilisateurs pour autoriser plusieurs sessions). Si le JWT a expiré (sur la base de sa revendication exp
), la base de données est vérifiée pour s'assurer que l'utilisateur est toujours valide (par exemple, compte non supprimé, mot de passe non modifié, etc.). Si l'utilisateur est valide, le jeton d'actualisation est vérifié et un nouveau jeton JWT/CSRF est généré et renvoyé dans la réponse. Si l'utilisateur n'est pas valide, le access_token
est renvoyé avec une valeur arbitraire comme 0
, et son expiration est réglée sur le passé afin que le navigateur l'efface. Le jeton CSRF est renvoyé vide, il sera donc effacé de localStorage. Tous les jetons d'actualisation pour l'utilisateur sont supprimés de la base de données.
(version courte):
exp
Vous semblez mélanger plusieurs technologies opposées différentes et ne précisez pas pourquoi vous avez choisi ces technologies et pourquoi elles contrôlent les menaces contre lesquelles vous essayez de vous protéger.
Les JWT sont stockés dans un cookie access_token. Ils sont d'abord signés puis chiffrés à l'aide de jose-jwt.
Y a-t-il une raison pour laquelle ils sont cryptés? La signature est utilisée lorsque vous souhaitez vérifier l'intégrité des données - c'est-à-dire qu'elles n'ont été modifiées par personne sans la clé. Le cryptage est utilisé lorsque vous souhaitez protéger la confidentialité des données, c'est-à-dire qu'elles ne peuvent être lues par quiconque sans clé. S'il n'y a rien dans le jeton qui ne devrait pas pouvoir être lu par l'utilisateur final, il n'y a aucune raison de chiffrer.
Les JWT incluent une revendication cst qui stocke un jeton CSRF généré de manière aléatoire. Le jeton CSRF est envoyé dans le corps de la réponse lors de la connexion et lorsqu'un nouveau JWT est émis. Le jeton CSRF est stocké dans le stockage local du navigateur. Il est envoyé avec chaque demande et validé par rapport à la valeur dans le JWT.
Cela ressemble à une implémentation du contrôle Double Submit Cookie CSRF. localStorage est protégé par même politique d'origine empêchant une autre session Web dans le navigateur de l'utilisateur d'accéder au jeton.
Assurez-vous que le jeton CSRF a au moins 128 bits d'entropie.
Comme les JWT expirent dans 30 minutes, il est nécessaire de les rafraîchir. Le JWT inclut une revendication rfs qui stocke un jeton d'actualisation aléatoire. Ce jeton d'actualisation est également stocké dans la base de données (une table distincte des utilisateurs pour autoriser plusieurs sessions). Si le JWT a expiré (sur la base de sa revendication exp), la base de données est vérifiée pour s'assurer que l'utilisateur est toujours valide (par exemple, compte non supprimé, mot de passe non modifié, etc.). Si l'utilisateur est valide, le jeton d'actualisation est vérifié et un nouveau jeton JWT/CSRF est généré et renvoyé dans la réponse.
C'est là que les choses se mélangent. Votre modèle de sécurité doit décider des sessions côté client ou des sessions côté serveur. Les JWT sont généralement employés pour les premiers. Autrement dit, si vous avez un jeton dans le client signé par une clé uniquement disponible pour le serveur, votre application doit avoir confiance que la présence de ce jeton indique une session valide si elle n'a pas expiré et que la signature est valide.
L'inconvénient de cette méthode est qu'il est difficile de révoquer des jetons. Si un compte d'utilisateur est compromis et qu'un utilisateur modifie son mot de passe, toutes les sessions de l'attaquant seront toujours actives pendant 30 minutes car elles disposent toujours d'un jeton valide, non expiré et signé.
Le correctif consiste à implémenter des sessions côté serveur à la place. Par exemple, vous gardez une trace des sessions dans une table de base de données, ce qui signifie qu'elles peuvent être déconnectées instantanément en supprimant la ligne dans la base de données. Le jeton de session peut être une chaîne aléatoire d'entropie de 128 bits, fournie sous forme de cookie côté client et stockée hachée avec SHA-256 côté serveur pour atténuer toute fuite de données de votre base de données. Vous pouvez toujours envoyer une date d'expiration en texte brut avec votre cookie, afin que le client sache que lorsqu'il doit actualiser le jeton. par exemple. un cookie HttpOnly pour le jeton et un cookie non HttpOnly contenant l'expiration afin que le JavaScript côté client puisse le lire. Les cookies HttpOnly peuvent aider à atténuer l'impact d'une faille XSS.
Par conséquent, si vous effectuez le suivi des sessions côté serveur, il y a peu d'avantages à disposer d'un côté client JWT signé. Un code supplémentaire signifie plus de surface d'attaque et plus de chances d'introduire des vulnérabilités dans le code excédentaire. La complexité d'une application est souvent inverse de la sécurité.
Si vous utilisez Double Submit Cookie en combinaison avec une session côté serveur pour l'état de la session, il est judicieux d'utiliser un cookie différent pour le jeton CSRF. Cela vous permettra d'ajouter le jeton CSRF en tant qu'en-tête en utilisant du code côté client sans risquer votre jeton d'identifiant de session. Notez que si vous définissez des en-têtes personnalisés et n'implémentez pas CORS, alors cela peut atténuer quelque peu CSRF . Il est également recommandé d'utiliser un jeton, car des technologies comme Flash ont tendance à perturber la sécurité du navigateur (Flash signifie plus de code en cours d'exécution, plus de code donne plus de surface d'attaque, plus de code signifie plus de risques de vulnérabilités).
Cela me semble assez solide (la solution CSRF est mignonne aussi) jusqu'à la partie Refresh Token.
L'un des avantages que je vois dans un système basé sur JWT est que les jetons d'accès expirent régulièrement. Cela signifie qu'un jeton d'accès JWT compromis ne donne accès à un attaquant que pendant une période de quelques minutes ou heures.
Si vous incluez le jeton d'actualisation dans le JWT, un attaquant qui compromet le JWT peut facilement l'utiliser pour s'actualiser lui-même, ce qui ne lui donne aucune date d'expiration effective.
Un jeton d'accès ne doit pas pouvoir accorder un nouveau jeton d'accès. Seul un jeton d'actualisation (ou une authentification complète) doit pouvoir accorder un nouveau jeton d'accès.
Agrégation des commentaires:
Quelques autres choses à faire: