web-dev-qa-db-fra.com

Meilleures pratiques pour la gestion côté serveur des jetons JWT

(engendré par ce fil puisqu'il s'agit vraiment d'une question qui lui est propre et qui n'est pas spécifique à NodeJS, etc.)

J'implémente un serveur API REST avec authentification) et j'ai implémenté avec succès la gestion des jetons JWT afin qu'un utilisateur puisse se connecter via un noeud final/login avec un nom d'utilisateur/mot de passe, sur lequel se trouve un jeton JWT. généré à partir d'un secret de serveur et renvoyé au client.Le jeton est ensuite transmis du client au serveur dans chaque demande d'API authentifiée, sur lequel le secret de serveur est utilisé pour vérifier le jeton.

Cependant, j'essaie de comprendre les meilleures pratiques pour savoir exactement comment et dans quelle mesure le jeton doit être validé pour créer un système véritablement sécurisé. Exactement ce qui devrait être impliqué dans la "validation" du jeton? Est-il suffisant que la signature puisse être vérifiée à l'aide du secret du serveur ou dois-je également vérifier que le jeton et/ou la charge de jeton sont croisés avec certaines données stockées sur le serveur?

Un système d'authentification basé sur un jeton ne sera aussi sûr que de transmettre un nom d'utilisateur/mot de passe dans chaque demande, à condition qu'il soit tout aussi difficile, voire plus difficile, d'obtenir un jeton que d'obtenir le mot de passe de l'utilisateur. Cependant, dans les exemples que j'ai vus, la seule information nécessaire pour produire un jeton est le nom d'utilisateur et le secret côté serveur. Cela ne signifie-t-il pas que si un utilisateur malveillant acquiert la connaissance du secret du serveur pendant une minute, il peut désormais produire des jetons pour le compte de tout utilisateur , ayant ainsi accès non seulement à un utilisateur donné, comme ce serait le cas si un mot de passe était obtenu, mais en fait à tous les comptes ?

Cela m'amène aux questions:

1) La validation de jeton JWT doit-elle être limitée à la vérification de la signature du jeton lui-même, en s’appuyant uniquement sur l’intégrité du secret du serveur ou à un mécanisme de validation distinct?

  • Dans certains cas, j'ai constaté l'utilisation combinée de jetons et de sessions serveur, qui établit une session en cas de connexion réussie via le point de terminaison/login. Les demandes d'API valident le jeton et comparent également les données décodées trouvées dans le jeton avec certaines données stockées dans la session. Cependant, utiliser des sessions signifie utiliser des cookies et, dans un certain sens, cela va à l’encontre de l’utilisation d’une approche basée sur les jetons. Cela peut également causer des problèmes à certains clients.

  • On pourrait imaginer que le serveur garde tous les jetons actuellement utilisés dans un memcache ou similaire, afin de garantir que même si le secret du serveur est compromis de sorte qu'un attaquant puisse produire des jetons "valides", seuls les jetons exacts générés via le noeud final/login serait accepté. Est-ce raisonnable ou simplement redondant/excessif?

2) Si la vérification de la signature JWT est le seul moyen de valider les jetons, c'est-à-dire que l'intégrité du secret du serveur est le point de rupture, comment doit-on gérer les secrets du serveur? Lecture à partir d'une variable d'environnement et création (aléatoire?) Une fois par pile déployée? Renouvelez ou faites pivoter périodiquement (et si oui, comment gérer les jetons valides existants qui ont été créés avant la rotation mais doivent être validés après la rotation, il est peut-être suffisant que le serveur conserve le secret actuel et précédent à un moment donné) ? Autre chose?

Peut-être suis-je simplement trop paranoïaque quand il s'agit de compromettre le secret du serveur, ce qui est bien sûr un problème plus général qui doit être résolu dans toutes les situations cryptographiques ...

102
JHH

J'ai également joué avec des jetons pour mon application. Bien que je ne sois en aucun cas un expert, je peux partager certaines de mes expériences et réflexions sur le sujet.

L'intérêt des JWT est essentiellement l'intégrité. Il fournit un mécanisme permettant à votre serveur de vérifier que le jeton qui lui a été fourni est authentique et qu’il a été fourni par votre serveur. La signature générée via votre secret est ce qui prévoit cela. Donc, oui, si votre secret est divulgué d'une manière ou d'une autre, cette personne peut générer des jetons que votre serveur pense être les siens. Un système basé sur un jeton serait toujours plus sécurisé que votre système de nom d'utilisateur/mot de passe simplement en raison de la vérification de la signature. Et dans ce cas, si quelqu'un a de toute façon votre secret, votre système a d'autres problèmes de sécurité à traiter que celui qui fabrique de faux jetons (et même dans ce cas, le simple changement du secret garantit que tous les jetons créés avec l'ancien secret sont maintenant invalides).

En ce qui concerne les données utiles, la signature vous indiquera seulement que le jeton qui vous a été fourni était exactement tel qu'il était lorsque votre serveur l'a envoyé. Il vous appartient évidemment de vérifier que le contenu des données utiles est valide ou approprié pour votre application.

Pour vos questions:

1.) Dans mon expérience limitée, il est certainement préférable de vérifier vos jetons avec un deuxième système. La validation de la signature signifie simplement que le jeton a été généré avec votre secret. Stocker tous les jetons créés dans une sorte de base de données (redis, memcache/sql/mongo ou un autre stockage) est un moyen fantastique de garantir que vous n'acceptez que les jetons créés par votre serveur. Dans ce scénario, même si votre secret est divulgué, cela n'aura pas d'importance, car les jetons générés ne seront de toute façon pas valides. C’est l’approche que j’adopte avec mon système: tous les jetons générés sont stockés dans une base de données (redis) et à chaque demande, je vérifie que le jeton se trouve dans ma base de données avant de l’accepter. De cette façon, les jetons peuvent être révoqués pour une raison quelconque, tels que les jetons qui ont été relâchés dans la nature, la déconnexion d'un utilisateur, les modifications de mot de passe, les modifications secrètes, etc.

2.) C’est un domaine dans lequel je n’ai pas beaucoup d’expérience et sur lequel je m’étudie activement car je ne suis pas un professionnel de la sécurité. Si vous trouvez des ressources, n'hésitez pas à les poster ici! Actuellement, j'utilise simplement une clé privée que je charge à partir du disque, mais il est évident que cette solution est loin d'être la meilleure ou la plus sécurisée.

51
Akshay Dhalwala

Voici quelques éléments à prendre en compte lors de l'implémentation de JWT dans votre application:

  • Maintenez votre durée de vie JWT relativement courte et gérez sa durée de vie sur le serveur. Si vous ne le souhaitez pas et si vous avez besoin ultérieurement de davantage d'informations dans vos JWT, vous devrez prendre en charge deux versions ou attendre que vos anciens JWT aient expiré avant de pouvoir implémenter votre modification. Vous pouvez facilement le gérer sur le serveur si vous ne regardez que le champ iat du jwt et si vous ignorez le champ exp.

  • Envisagez d'inclure l'URL de la demande dans votre JWT. Par exemple, si vous souhaitez que votre JWT soit utilisé sur le noeud final /my/test/path, incluez un champ comme 'url':'/my/test/path' dans votre JWT, pour vous assurer qu'il n'est jamais utilisé que sur ce chemin. Sinon, vous constaterez peut-être que les utilisateurs commencent à utiliser vos JWT sur d'autres ordinateurs d'extrémité, même ceux pour lesquels ils n'ont pas été créés. Vous pouvez également envisager d'inclure un md5 (url) à la place, car avoir un gros url dans le JWT finira par rendre le JWT beaucoup plus gros, et ils peuvent devenir très gros.

  • L'expiration de JWT doit être configurable pour chaque cas d'utilisation si des JWT sont implémentés dans une API. Par exemple, si vous avez 10 points de terminaison pour 10 cas d'utilisation différents pour JWT, assurez-vous que vous pouvez faire en sorte que chaque point de terminaison accepte les JWT qui expirent à des moments différents. Cela vous permet de verrouiller certains points d'extrémité plus que d'autres, par exemple, si les données fournies par un point d'extrémité sont très sensibles.

  • Au lieu de simplement expirer les JWT après un certain temps, envisagez de mettre en œuvre des JWT prenant en charge les deux:

    • N utilisations - ne peuvent être utilisées que N fois avant leur expiration et
    • expire après un certain temps (si vous avez un jeton à usage unique, vous ne voulez pas qu'il soit éternel, s'il n'est pas utilisé, n'est-ce pas?)
  • Tous les échecs d'authentification JWT doivent générer un en-tête de réponse "erreur" qui explique pourquoi l'authentification JWT a échoué. par exemple. "expired", "il ne reste plus d'usages", "révoqué", etc. Cela aide les développeurs à comprendre pourquoi leur JWT échoue.

  • Pensez à ignorer l '"en-tête" de vos JWT lors de la fuite d'informations et donner une mesure de contrôle aux pirates. Ceci concerne principalement le champ alg de l'en-tête - ignorez-le et supposez simplement que l'en-tête correspond à ce que vous souhaitez prendre en charge, car cela évite aux pirates d'essayer d'utiliser l'algorithme None, qui supprime le contrôle de sécurité de la signature.

  • JWT devrait inclure un identifiant indiquant quelle application a généré le jeton. Par exemple, si vos fichiers JWT sont créés par 2 clients différents, mychat et myclassifiedsapp, chacun doit inclure son nom de projet ou un nom similaire dans le champ "iss" du fichier JWT, par exemple. "iss": "mychat"

  • Les fichiers JWT ne doivent pas être consignés dans les fichiers journaux. Le contenu d'un JWT peut être consigné, mais pas le JWT lui-même. Cela garantit que les développeurs ou les autres utilisateurs ne peuvent pas extraire les fichiers JWT des fichiers journaux et effectuer des opérations dans les comptes d'autres utilisateurs.
  • Assurez-vous que votre implémentation JWT n'autorise pas l'algorithme "None" afin d'éviter que les pirates informatiques créent des jetons sans les signer. Cette classe d'erreurs peut être entièrement évitée en ignorant "l'en-tête" de votre JWT.
  • Pensez fortement à utiliser iat (délivré à) au lieu de exp (expiration) dans vos fichiers JWT. Pourquoi? Étant donné que iat signifie fondamentalement à quel moment le JWT a été créé, cela vous permet de régler sur le serveur lorsque le JWT expire, en fonction de la date de création. Si quelqu'un passe dans un exp vieux de 20 ans, le JWT vit pour toujours! Notez que vous expirez automatiquement les JWT si leur iat est dans le futur, tout en laissant un peu de marge de manœuvre (par exemple, 10 secondes), au cas où le temps du client serait légèrement désynchronisé par rapport à celui du serveur.
  • Envisagez de mettre en œuvre un point de terminaison pour créer des fichiers JWT à partir d'une charge JSON et forcez tous vos clients en implémentation à utiliser ce point de terminaison pour créer leurs fichiers JWT. Cela garantit que vous pouvez résoudre facilement les problèmes de sécurité que vous souhaitez avec la façon dont les fichiers JWT sont créés à un endroit unique. Nous ne l'avons pas fait directement dans notre application et devons maintenant mettre en place des mises à jour de sécurité côté serveur JWT car nos 5 clients différents ont besoin de temps pour être mis en œuvre. En outre, faites en sorte que votre point de terminaison create accepte un tableau de charges utiles Json à créer par JWT, ce qui réduira le nombre de demandes http arrivant sur ce point de terminaison pour vos clients.
  • Si votre JWT doit être utilisé sur des ordinateurs d'extrémité prenant également en charge l'utilisation par session, veillez à ne rien mettre dans votre JWT nécessaire pour satisfaire la demande. Vous pouvez facilement le faire si vous vous assurez que votre ordinateur d'extrémité fonctionne avec une session, alors qu'aucun JWT n'est fourni.
  • Donc, généralement, les JWT finissent par contenir un ID utilisateur ou un ID de groupe, et permettent l'accès à une partie de votre système en fonction de ces informations. Assurez-vous que vous n'autorisez pas les utilisateurs d'une zone de votre application à emprunter l'identité d'autres utilisateurs, notamment si cela donne accès à des données sensibles. Pourquoi? Même si votre processus de génération JWT n’est accessible qu’aux services "internes", les développeurs ou d’autres équipes internes peuvent générer des JWT permettant d’accéder aux données de tout utilisateur, par exemple. le PDG de la société de certains clients au hasard. Par exemple, si votre application fournit un accès aux enregistrements financiers pour les clients, alors en générant un JWT, un développeur pourrait récupérer les enregistrements financiers de n'importe quelle entreprise! Et si un pirate informatique pénètre de toute façon dans votre réseau interne, il pourrait faire de même.
  • Si vous souhaitez autoriser la mise en cache de toute URL contenant un JWT, assurez-vous que les autorisations de différents utilisateurs sont incluses dans l'URL et non du JWT. Pourquoi? Parce que les utilisateurs peuvent obtenir des données, ils ne le devraient pas. Par exemple, supposons qu'un super utilisateur se connecte à votre application et demande l'URL suivante: /mysite/userInfo?jwt=XXX, et que cette URL est mise en cache. Ils se déconnectent et quelques minutes plus tard, un utilisateur régulier se connecte à votre application. Ils obtiendront le contenu mis en cache - avec des informations sur un super utilisateur! Cela a tendance à se produire moins sur le client, et davantage sur le serveur, en particulier dans les cas où vous utilisez un CDN comme Akamai et où certains fichiers durent plus longtemps. Cela peut être corrigé en incluant les informations utilisateur pertinentes dans l'URL et en les validant sur le serveur, même pour les demandes mises en cache, par exemple /mysite/userInfo?id=52&jwt=XXX
  • Si votre jwt est destiné à être utilisé comme cookie de session et ne doit fonctionner que sur la même machine pour laquelle le jwt a été créé, vous devez envisager d'ajouter un champ jti à votre jwt. Il s'agit essentiellement d'un jeton CSRF, qui garantit que votre JWT ne peut pas être transmis du navigateur d'un utilisateur à un autre.
38
Brad Parks

Je ne pense pas être un expert, mais j'aimerais partager quelques réflexions sur Jwt.

  • 1: Comme l'a dit Akshay, il est préférable d'avoir un deuxième système pour valider votre jeton.

    a .: La façon dont je le gère: je stocke le hachage généré dans un stockage de session avec le temps expiricy. Pour valider un jeton, il doit avoir été émis par le serveur.

    b.:Il y a au moins une chose qui doit être vérifiée la méthode de signature utilisée. par exemple :

    header :
    {
      "alg": "none",
      "typ": "JWT"
    }
    

Certaines bibliothèques validant JWT accepteraient celui-ci sans vérifier le hachage. Cela signifie que, sans savoir que votre sel avait l'habitude de signer le jeton, un pirate informatique pourrait s'octroyer des droits. Assurez-vous toujours que cela ne peut pas arriver. https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/

c .: L'utilisation d'un cookie avec un identifiant de session ne serait pas utile pour valider votre jeton. Si quelqu'un veut pirater la session d'un utilisateur lambda, il lui suffira d'utiliser un renifleur (par exemple: wirehark). Ce pirate aurait les deux informations en même temps.

  • 2: Il en va de même pour tous les secrets. Il y a toujours un moyen de le savoir.

La façon dont je le gère est liée au point 1.a. : J'ai un secret mélangé avec une variable aléatoire. Le secret est unique pour chaque jeton.

Cependant, j'essaie de comprendre les meilleures pratiques pour savoir exactement comment et dans quelle mesure le jeton doit être validé pour créer un système véritablement sécurisé.

Si vous voulez la meilleure sécurité possible, vous ne devez pas suivre aveuglément les meilleures pratiques. Le meilleur moyen est de comprendre ce que vous faites (je pense que ça va quand je vois votre question), puis d'évaluer la sécurité dont vous avez besoin. Et si le Mossad veut avoir accès à vos données confidentielles, il trouvera toujours un moyen. (J'aime ce billet de blog: https://www.schneier.com/blog/archives/2015/08/mickens_on_secu.html )

Beaucoup de bonnes réponses ici. Je vais intégrer certaines des réponses qui me semblent les plus pertinentes et ajouter quelques suggestions supplémentaires.

1) La validation de jeton JWT doit-elle être limitée à la vérification de la signature du jeton lui-même, en s’appuyant uniquement sur l’intégrité du secret du serveur ou à un mécanisme de validation distinct?

Non, pour des raisons indépendantes de la compromission d'un secret de jeton. Chaque fois qu'un utilisateur se connecte via un nom d'utilisateur et un mot de passe, le serveur d'autorisation doit stocker soit le jeton généré, soit des métadonnées relatives au jeton généré. Considérez ces métadonnées comme un enregistrement d'autorisation. Un couple utilisateur/application donné ne devrait avoir qu'un seul jeton valide, ou autorisation, à un moment donné. Les métadonnées utiles sont l'ID utilisateur associé au jeton d'accès, l'ID d'application et l'heure à laquelle le jeton d'accès a été émis (ce qui permet la révocation des jetons d'accès existants et l'émission d'un nouveau jeton d'accès). Sur chaque demande d'API, vérifiez que le jeton contient les métadonnées appropriées. Vous devez conserver des informations sur la date d'émission de chaque jeton d'accès afin qu'un utilisateur puisse révoquer les jetons d'accès existants si les informations d'identification de son compte sont compromises, puis se reconnecter et commencer à utiliser un nouveau jeton d'accès. Cela mettra à jour la base de données avec l'heure à laquelle le jeton d'accès a été émis (l'heure d'autorisation créée). Sur chaque demande d'API, vérifiez que l'heure d'émission du jeton d'accès est postérieure à l'heure d'autorisation créée.

Parmi les autres mesures de sécurité, notons la non-journalisation des fichiers JWT et la nécessité d'un algorithme de signature sécurisé tel que SHA256.

2) Si la vérification de la signature JWT est le seul moyen de valider les jetons, c'est-à-dire que l'intégrité du secret du serveur est le point de rupture, comment doit-on gérer les secrets du serveur?

La compromission des secrets de serveur permettrait à un attaquant d'émettre des jetons d'accès pour tout utilisateur, et le stockage des données de jeton d'accès à l'étape 1 n'empêcherait pas nécessairement le serveur d'accepter ces jetons d'accès. Par exemple, supposons qu'un utilisateur ait reçu un jeton d'accès, puis qu'un attaquant génère ensuite un jeton d'accès pour cet utilisateur. L'heure d'autorisation du jeton d'accès serait valide.

Comme le dit Akshay Dhalwala, si le secret de votre serveur côté serveur est compromis, vous devrez faire face à de plus gros problèmes car cela signifie qu'un attaquant a compromis votre réseau interne, votre référentiel de code source, ou les deux.

Cependant, un système visant à limiter les dommages d'un secret de serveur compromis et à éviter de stocker des secrets dans le code source implique une rotation de jeton secret à l'aide d'un service de coordination tel que https://zookeeper.Apache.org . Utilisez un travail cron pour générer un secret d'application toutes les quelques heures environ (quelle que soit la durée de validité de vos jetons d'accès) et transmettez le secret mis à jour à Zookeeper. Sur chaque serveur d'applications ayant besoin de connaître le secret du jeton, configurez un client ZK mis à jour chaque fois que la valeur du noeud ZK change. Stockez un secret principal et un secret secondaire, et chaque fois que le secret du jeton est modifié, définissez le nouveau secret du jeton sur le principal et l'ancien secret du jeton sur le secondaire. De cette façon, les jetons valides existants seront toujours valides car ils seront validés par rapport au secret secondaire. Au moment où le secret secondaire est remplacé par l'ancien secret principal, tous les jetons d'accès émis avec le secret secondaire seraient de toute façon expirés.

3
skeller88
0
SPoint