83 votes

Comment mettre en œuvre la réinitialisation des mots de passe ?

Je travaille sur une application en ASP.NET, et je me demandais spécifiquement comment je pouvais mettre en œuvre un système de gestion de l'information de l'entreprise. Password Reset si je voulais créer ma propre fonction.

Plus précisément, j'ai les questions suivantes :

  • Quel est le meilleur moyen de générer un identifiant unique difficile à pirater ?
  • Devrait-il y avoir une minuterie ? Si oui, quelle doit être sa durée ?
  • Dois-je enregistrer l'adresse IP ? Est-ce vraiment important ?
  • Quelles informations dois-je demander sur l'écran "Réinitialisation du mot de passe" ? Juste l'adresse e-mail ? Ou peut-être l'adresse électronique plus une information qu'ils "connaissent" ? (Équipe préférée, nom du chiot, etc.)

Y a-t-il d'autres considérations dont je dois tenir compte ?

NB : Autres questions ont escamoté la mise en œuvre technique entièrement. En effet, la réponse acceptée passe sous silence les détails sanglants. J'espère que cette question et les réponses qui suivront entreront dans les détails sanglants, et j'espère qu'en formulant cette question de manière beaucoup plus précise, les réponses seront moins "futiles" et plus "sanglantes".

Modifier : Les réponses qui expliquent également comment une telle table serait modélisée et traitée dans SQL Server ou tout lien ASP.NET MVC vers une réponse seraient appréciés.

0 votes

ASP.NET MVC utilise le fournisseur d'authentification ASP.NET par défaut, donc tous les échantillons de code que vous trouverez autour de ce fournisseur ne devraient pas être pertinents pour vos besoins.

68voto

eduncan911 Points 5417

EDIT 2012/05/22 : Pour faire suite à cette réponse populaire, je n'utilise plus moi-même les GUIDs dans cette procédure. Comme l'autre réponse populaire, j'utilise maintenant mon propre algorithme de hachage pour générer la clé à envoyer dans l'URL. Cette méthode a également l'avantage d'être plus courte. Regardez dans System.Security.Cryptography pour les générer, que j'utilise aussi habituellement un SALT.

Tout d'abord, ne réinitialisez pas immédiatement le mot de passe de l'utilisateur.

Premièrement, ne réinitialisez pas immédiatement le mot de passe de l'utilisateur lorsqu'il le demande. Il s'agit d'une violation de la sécurité, car quelqu'un pourrait deviner les adresses électroniques (c'est-à-dire votre adresse électronique à l'entreprise) et réinitialiser les mots de passe sur un coup de tête. Les meilleures pratiques actuelles incluent généralement un lien de "confirmation" envoyé à l'adresse électronique de l'utilisateur, confirmant qu'il souhaite réinitialiser son mot de passe. C'est sur ce lien que vous voulez envoyer le lien de la clé unique. J'envoie le mien avec un lien comme : domain.com/User/PasswordReset/xjdk2ms92

Oui, définissez un délai d'attente sur le lien et stockez la clé et le délai d'attente sur votre backend (et le sel si vous en utilisez un). Des délais de 3 jours sont la norme, et assurez-vous de notifier l'utilisateur de ces 3 jours au niveau du web lorsqu'il demande une réinitialisation.

Utilisez une clé de hachage unique

Ma réponse précédente disait d'utiliser un GUID. Je la modifie maintenant pour conseiller à tout le monde d'utiliser un hachage généré de manière aléatoire, par exemple en utilisant la fonction RNGCryptoServiceProvider . Et assurez-vous d'éliminer tous les "vrais mots" du hachage. Je me souviens d'un appel téléphonique spécial à 6 heures du matin où une femme a reçu un certain mot "c" dans sa clé hachée "supposée être aléatoire" qu'un développeur avait faite. Doh !

Procédure complète

  • L'utilisateur clique sur "réinitialiser" le mot de passe.
  • Il est demandé à l'utilisateur de fournir une adresse électronique.
  • L'utilisateur saisit l'email et clique sur envoyer. Ne confirmez pas ou ne refusez pas l'e-mail car c'est également une mauvaise pratique. Dites simplement : "Nous avons envoyé une demande de réinitialisation du mot de passe si l'adresse électronique est vérifiée" ou quelque chose d'aussi cryptique.
  • Vous créez un hachage à partir du RNGCryptoServiceProvider et le stocker comme une entité distincte dans un ut_UserPasswordRequests et renvoie à l'utilisateur. Ainsi, vous pouvez suivre les anciennes demandes et informer l'utilisateur que les anciens liens ont expiré.
  • Envoyez le lien à l'e-mail.

L'utilisateur obtient le lien, comme http://domain.com/User/PasswordReset/xjdk2ms92 et clique dessus.

Si le lien est vérifié, vous demandez un nouveau mot de passe. C'est simple, et l'utilisateur peut définir son propre mot de passe. Ou bien, définissez votre propre mot de passe cryptique ici et informez-les de leur nouveau mot de passe ici (et envoyez-le par courriel).

1 votes

Je me demandais, si le mot de passe réel de l'utilisateur est haché, pourquoi générer une nouvelle clé HASH ? Ne serait-il pas correct d'envoyer un email à l'utilisateur avec un lien pour réinitialiser le mot de passe en passant par le mot de passe haché ? Le mot de passe haché ne peut pas être réinitialisé, lorsque l'utilisateur clique sur le lien, le serveur recevra le mot de passe haché, le comparera avec le mot de passe réel stocké, puis permettra à l'utilisateur de changer de mot de passe.

0 votes

Et une autre chose assez bonne à ce sujet, est que vous n'avez pas besoin de définir le délai d'attente, une fois que l'utilisateur a changé le mot de passe, l'ancien lien ne sera automatiquement plus valide, parce que le mot de passe haché stocké dans la base de données a été changé.

1 votes

Daniel, c'est une très mauvaise idée. Je pense que tu dois chercher sur Google le terme "attaques par force brute". De plus, la raison pour laquelle vous voulez que le mot de passe expire est que si l'e-mail de quelqu'un est compromis un an plus tard (et qu'il ne l'a jamais réinitialisé), le pirate obtient le droit de changer le mot de passe.

66voto

AviD Points 8413

Beaucoup de bonnes réponses ici, je ne prendrai pas la peine de tout répéter...

Sauf pour une question, qui est répétée par presque toutes les réponses ici, même si elle est fausse :

Les identifiants sont (de manière réaliste) uniques et statistiquement impossibles à deviner.

Ce n'est pas vrai, les GUIDs sont des identifiants très faibles, et devraient PAS être utilisé pour autoriser l'accès au compte d'un utilisateur.
Si vous examinez la structure, vous obtenez un total de 128 bits au maximum... ce qui n'est pas considéré comme beaucoup de nos jours.
La première moitié est un invariant typique (pour le système générateur), et la moitié restante dépend du temps (ou quelque chose de similaire).
Dans l'ensemble, il s'agit d'un mécanisme très faible et facile à forcer.

Alors n'utilisez pas ça !

Au lieu de cela, il suffit d'utiliser un générateur de nombres aléatoires cryptographiquement fort ( System.Security.Cryptography.RNGCryptoServiceProvider ), et obtenir au moins 256 bits d'entropie brute.

Tout le reste, comme les nombreuses autres réponses fournies.

6 votes

Tout à fait d'accord, pour autant que je sache, les GUID n'ont jamais été conçus pour être cryptographiquement forts et impossibles à deviner.

5 votes

Bien dit, AFAIK MSDN indique clairement que les GUID ne doivent pas être utilisés pour la sécurité.

2 votes

Les UUID de la version 4 sont utilisés dans Windows depuis 2000 : Comment les GUID de .NET 4 sont-ils générés ? - Dépassement de pile . Ils contiennent 122 bits aléatoires, ce qui, je pense, est conforme aux recommandations du NIST. Il y avait une très mauvaise vulnérabilité à une attaque locale, qui selon CryptGenRandom - Wikipédia a été corrigé dans Vista et XP en 2008. Où voyez-vous donc des problèmes dans l'utilisation actuelle des GUID ?

8voto

jmucchiello Points 10521

Tout d'abord, nous devons savoir ce que vous savez déjà sur l'utilisateur. De toute évidence, vous avez un nom d'utilisateur et un ancien mot de passe. Que savez-vous d'autre ? Avez-vous une adresse électronique ? Avez-vous des données concernant la fleur préférée de l'utilisateur ?

En supposant que vous disposez d'un nom d'utilisateur, d'un mot de passe et d'une adresse électronique de travail, vous devez ajouter deux champs à votre table utilisateur (en supposant qu'il s'agisse d'une table de base de données) : une date appelée new_passwd_expire et une chaîne de caractères new_passwd_id.

En supposant que vous disposez de l'adresse électronique de l'utilisateur, lorsque quelqu'un demande une réinitialisation de son mot de passe, vous mettez à jour la table des utilisateurs comme suit :

new_passwd_expire = now() + some number of days
new_passwd_id = some random string of characters (see below)

Ensuite, vous envoyez un courriel à l'utilisateur à cette adresse :

Cher monsieur et madame

Quelqu'un a demandé un nouveau mot de passe pour le compte d'utilisateur <nom d'utilisateur> à <votre nom de site web>. Si vous avez demandé cette réinitialisation de mot de passe, suivez ce lien :

http://example.com/yourscript.lang?update=<new_password_id >

Si ce lien ne fonctionne pas, vous pouvez vous rendre à l'adresse suivante http://example.com/yourscript.lang et saisissez le texte suivant dans le formulaire : <nouveau_mot_de_passe>

Si vous n'avez pas demandé de réinitialisation de votre mot de passe, vous pouvez ignorer cet e-mail.

Merci, yada yada

Maintenant, le codage de votre script.lang : Ce script a besoin d'un formulaire. Si la var update est passée sur l'URL, le formulaire demande juste le nom d'utilisateur et l'adresse email de l'utilisateur. Si update n'est pas passé, il demande le nom d'utilisateur, l'adresse email, et le code d'identification envoyé dans l'email. Vous demandez également un nouveau mot de passe (deux fois bien sûr).

Pour vérifier le nouveau mot de passe de l'utilisateur, vous vérifiez que le nom d'utilisateur, l'adresse électronique et le code d'identification correspondent tous, que la demande n'a pas expiré et que les deux nouveaux mots de passe correspondent. En cas de succès, vous changez le mot de passe de l'utilisateur par le nouveau mot de passe et effacez les champs de réinitialisation de mot de passe de la table des utilisateurs. Veillez également à déconnecter l'utilisateur/effacer tous les cookies liés à la connexion et à rediriger l'utilisateur vers la page de connexion.

Essentiellement, le champ new_passwd_id est un mot de passe qui ne fonctionne que sur la page de réinitialisation du mot de passe.

Une amélioration possible : vous pourriez supprimer <nom d'utilisateur> de l'email. "Quelqu'un a demandé une réinitialisation du mot de passe pour un compte à cette adresse email...." Faisant ainsi du nom d'utilisateur quelque chose que seul l'utilisateur connaît si l'email est intercepté. Je n'ai pas commencé de cette façon car si quelqu'un attaque le compte, il connaît déjà le nom d'utilisateur. Cette obscurité supplémentaire empêche les attaques de type "man-in-the-middle" au cas où une personne malveillante intercepterait l'e-mail.

Quant à vos questions :

générant la chaîne aléatoire : Il n'est pas nécessaire qu'elle soit extrêmement aléatoire. N'importe quel générateur de GUID ou même md5(concat(salt,current_timestamp())) est suffisant, où salt est quelque chose sur l'enregistrement de l'utilisateur comme la date de création du compte. Ce doit être quelque chose que l'utilisateur ne peut pas voir.

timer : Oui, vous en avez besoin pour garder votre base de données saine. Pas plus d'une semaine est vraiment nécessaire, mais au moins deux jours, car on ne sait jamais combien de temps peut durer le retard d'un courriel.

Adresse IP : Comme le courrier électronique peut être retardé de plusieurs jours, l'adresse IP n'est utile que pour l'enregistrement, pas pour la validation. Si vous voulez l'enregistrer, faites-le, sinon vous n'en avez pas besoin.

Écran de réinitialisation : Voir ci-dessus.

J'espère que c'est suffisant. Bonne chance.

0 votes

Un attaquant potentiel ne serait-il pas en mesure d'utiliser le MD5 de l'horodatage actuel pour s'introduire ?

0 votes

Je vous déconseille fortement d'envoyer un mot de passe dans un courriel par voie électronique. La plupart des utilisateurs ne suppriment pas ces messages, ce qui constitue une violation de la sécurité. Certains d'entre eux préfèrent le copier-coller à chaque fois dans leurs messages "favoris". Que se passe-t-il si le certificat du serveur de messagerie de l'entreprise de l'utilisateur est expiré et que le trafic est reniflé ? Pour minimiser cette violation possible, il faut (1) fixer un court délai d'expiration pour ce mot de passe particulier - 1 heure, et (2) obliger l'utilisateur à le mettre à jour lors de sa prochaine connexion.

0 votes

Ognyan, le mot de passe envoyé par email ne fonctionne qu'une fois. Ils doivent changer leur mot de passe après la connexion et l'email ne contient pas le nom de connexion de l'utilisateur. Donc, non, ils ne peuvent pas simplement le copier-coller à chaque fois. Ne pas supprimer l'email n'est pas un problème de sécurité puisqu'il s'agit juste d'une chaîne de lettres/chiffres sans signification qui ne rapportera RIEN à l'attaquant après la réinitialisation du mot de passe.

3voto

E.J. Brennan Points 12485

Un GUID envoyé à l'adresse électronique enregistrée est probablement suffisant pour la plupart des applications courantes - avec un délai d'attente, c'est encore mieux.

Après tout, si la boîte aux lettres électronique de l'utilisateur a été compromise (c'est-à-dire qu'un pirate dispose de l'identifiant et du mot de passe de l'adresse électronique), il n'y a pas grand-chose que vous puissiez faire.

2voto

Sergej Andrejev Points 4052

Vous pourriez envoyer un courriel à l'utilisateur avec un lien. Ce lien contiendrait une chaîne difficile à deviner (comme un GUID). Côté serveur, vous stockez également la même chaîne que celle envoyée à l'utilisateur. Maintenant, lorsque l'utilisateur appuie sur le lien, vous pouvez trouver dans votre base de données l'entrée avec la même chaîne secrète et réinitialiser son mot de passe.

0 votes

Plus de détails seraient utiles.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X