web-dev-qa-db-fra.com

Comment créer correctement un jeton de réinitialisation de mot de passe?

J'ai cherché pendant longtemps et je n'ai jamais vraiment trouvé d'articles ou de messages qui expliquent complètement comment un jeton de réinitialisation de mot de passe devrait être créé.

Ce que je sais jusqu'à présent:

  • Cela devrait être aléatoire.
  • Son hachage doit être stocké dans la base de données.
  • Il devrait expirer après un certain temps.

Ce dont je ne suis pas trop sûr:

  1. Dois-je utiliser une méthode cryptographique pour créer le jeton aléatoire? Est le noeud crypto.randomBytes assez?
  2. Dois-je utiliser une méthode cryptographique (par exemple, bcrypt) pour hacher le jeton?
  3. Dois-je inclure certaines informations (c'est-à-dire l'heure d'expiration, l'UUID, etc.) dans le jeton? Si oui, JWT est-il un bon moyen?

J'imagine que l'ensemble du processus ressemblerait à ceci:

  1. Créez un jeton aléatoire.
  2. Hachez le jeton et stockez le hachage dans la base de données.
  3. Envoyez le jeton en texte brut au client.
  4. Une fois qu'il est utilisé, ou s'il n'est pas utilisé après un certain délai, supprimez-le de la base de données.
  5. Lorsqu'un jeton est utilisé, vérifiez si son hachage correspond à celui de la base de données.
  6. S'il correspond, autorisez l'utilisateur à réinitialiser le mot de passe. Sinon, interdisez le processus.
5
yqlim

Les jetons de réinitialisation de mot de passe ne ressemblent pas beaucoup aux mots de passe. Ils sont de courte durée, à usage unique et - plus pertinent ici - générés par machine et non mémorisables.

  1. Vous devez absolument utiliser un générateur de nombres aléatoires cryptographiquement sécurisé pour produire le jeton. crypto.randomBytes est assez bon, oui. Assurez-vous que le jeton est suffisamment long; quelque chose de l'ordre de 16 octets (128 bits) devrait fonctionner.
  2. Parce que la "préimage" (la valeur introduite dans la fonction de hachage, dans ce cas le jeton) est aléatoire, il n'est pas nécessaire d'utiliser un hachage lent. Les hachages lents sont destinés à la protection par force brute, car les mots de passe sont nuls et ne prennent généralement pas longtemps (en termes de machine) à deviner; Les valeurs aléatoires de 128 bits ne sont pas contraignables par brute pour toute signification pratique du terme. Faites juste un seul tour de SHA-256 ou similaire.
  3. Mauvaise idée; ça complique les choses. Stockez tous ces trucs dans la base de données à la place. Oui, vous pouvez le faire sans état en le mettant dans un JWT, mais la DB est beaucoup plus logique:
    • Vous devrez de toute façon interagir avec la base de données pour réinitialiser le mot de passe.
    • Vous devrez de toute façon contourner toute mise en cache; même si vous avez un système distribué, ils auront tous besoin de voir ce changement, il est donc inutile de le rendre apatride.
    • Il vous suffit de faire une lecture de base de données pour vérifier le jeton (à la fois que sa valeur est correcte et qu'il n'a pas encore expiré, ce qui peut être fait en une seule requête), ce qui est un événement relativement rare; ce n'est pas comme avoir besoin de faire une recherche de jeton de session à chaque demande.
    • Les JWT ont expiré mais il n'y a aucun moyen pratique de les rendre à usage unique (vous devez stocker l'état en disant "ces JWT ne sont plus valides" sur le serveur jusqu'à ce que chacun expire, ce qui manque le point des JWT.) Jetons de réinitialisation de mot de passe.) stockés dans la base de données peuvent (et doivent) être à usage unique en les supprimant lors de la vérification.
    • Vous pouvez avoir une tâche de nettoyage qui passe et supprime périodiquement les jetons expirés (qui n'ont jamais été utilisés), si vous le souhaitez. Cependant, ils n'occupent pas beaucoup d'espace dans la base de données (un condensé de hachage et un horodatage sur chaque utilisateur, si vous ne voulez qu'un seul par utilisateur valide à la fois, ou un condensé de hachage, un horodatage et une clé étrangère dans la table Utilisateurs si vous souhaitez permettre à plusieurs jetons d'être valides à la fois pour un utilisateur; les deux présentent des avantages et des inconvénients).
    • Les JWT ont plus de vulnérabilités potentielles (quelqu'un vole votre clé de signature, quelqu'un découvre que la clé a été générée de manière non sécurisée et n'a pas été tournée depuis que le bogue a été corrigé, votre bibliothèque JWT est vulnérable à une confusion de clé ou d'algorithme, etc.) par rapport à la simple vérification la base de données (qui est vulnérable à pratiquement rien sauf SQLi, je suppose, que vous savez, espérons-le, comment éviter).

Votre processus de base est logique.

4
CBHacking

1. Oui, vous devez utiliser une méthode cryptographique pour générer le jeton. Utilisez toujours l'aléa cryptographique chaque fois que vous faites quelque chose qui est lié à la sécurité. Oui, crypto.randomBytes c'est bien. Utilisez 16 octets (vous pourriez vous en tirer avec un peu moins, mais ne prenez pas de risques à moins que le jeton ne doive absolument être tapé plutôt que simplement cliqué ou copié-collé). (16 octets se traduisent par 24 caractères de Base64 ou 32 chiffres hexadécimaux.)

2. Hachez le jeton avec un hachage cryptographique tel que SHA-256 ou SHA-512. Vous n'avez pas besoin d'un hachage de mot de passe ici: les hachages de mot de passe tels que bcrypt sont pour quand l'entrée est un mot de passe dont un humain se souvient, et sont inutiles lorsque l'entrée est une chaîne générée aléatoirement de suffisamment moins. Un hachage de mot de passe est un peu plus difficile à utiliser et beaucoup plus lent, et vous n'en avez pas besoin ici.

3. Je ne vois aucun avantage à ajouter des informations dans le jeton. Les informations telles que la date d'expiration et à quoi sert le jeton doivent de toute façon être dans la base de données.