Mot de passe perdu -> envoi nouveau mot de passe

a marqué ce sujet comme résolu.

Bonjour, ça fait seulement quelques semaines que je débutent en PHP, donc je reviens vous demander un peu d'aide SVP.

En gros, dans un admin (petit CMS) que je suis en train de faire, il y a une rubrique utilisateur: avec possibilité d'ajouter de nouveaux utilisateurs (avec sha1 pour crypter mot de passe), et que les utilisateurs puissent modifier leurs mots de passes, pseudo, mail…

_Pour se connecter à l'admin (dans la page login.php), l'utilisateur doit entrer son pseudo avec son mot de passe. Je souhaiterai que si l'utilisateur oubli son mot de passe, qu'il puisse en entrant son adresse mail (adresse mail valide qui est dans la BDD) -> récupérer un nouveau mot de passe (qu'il pourra modifier par la suite dans l'admin si il le souhaite).

_Est-ce possible de m'expliquer le principe SVP? car dans Google je n'ai pas trouvé grand chose, et je ne voit vraiment pas le principe de comment je peut lui envoyer automatiquement un nouveau mot de passe dans son adresse mail qu'il pourra ensuite s'en servir pour se connecter.

Voici mon code actuelle (où j'ai encore beaucoup de boulot je pense):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
if(!empty($_POST['submit'])) {

    $mail = htmlspecialchars(addslashes($_POST['mail']));

    if(!empty($mail)) {

        $db = Connexion();      // conexion à bdd
        $requete = $db->prepare("SELECT * FROM utilisateurs WHERE mail = :mail LIMIT 1");
        $requete->execute(array('mail'=>$mail));

        if ($requete->fetchColumn() != 0) {             // si mail existe dans BDD

            $info_loginIDp = '<span class="vert bold">Adresse mail OK</span>';

            $mail = 'admin@admin.admin';
            $destinataire = 'destinataire@live.fr';
            $sujet = 'ID perdus ?';
            $entete = 'From: '.$mail.''."\r\n";
            $entete .= 'Content-Type: text/html; charset="utf-8"'."\n";                     // ".=" pour continuer la ligne du dessus. "\n" : Pour retour à la ligne
            $entete .= 'Content-Transfer-Encoding: 8bit';
            $message = 'Vous avez demandé a réinitialiser vos identifiants, voici un nouveaux mot de passe :...';

            if(mail($destinataire, $sujet, $message, $entete)) {                             // si mail vaut true (si mail a été envoyé)
                $info_loginIDp = '<span class="vert bold">Un message vous a été envoyé</span>';
            }
            else {
                $erreur_loginIDp = '<span class="rouge bold">Une erreur est survenue</span>';
            }
        }
        else {
            $erreur_loginIDp = '<span class="rouge bold">Adresse mail incorect !</span>';
        }
        $requete->closeCursor();
    }
    else {
        $erreur_loginIDp = '<span class="rouge bold">Veuillez remplit le champs</span>';
    }
}

Merci beaucoup

+0 -0

Avant toute chose je tiens à souligner que sha1 ne suffit pas à être sécurisé, surtout si tu n'ajoutes pas de sel.


Bref, passons au cœur du sujet !

L'idée c'est que si ton utilisateur existe (ligne 11), tu génères une chaîne aléatoire qui servira de nouveau mot de passe (à toi de voir quels caractères tu veux utiliser et la longueur à obtenir : tu trouveras des petits script — snippets pour les intimes — facilement sur ton moteur de recherche préféré).

Une fois que tu as ce nouveau mot de passe, tu peux enregistrer le hash (le résultat de sha1 ici) dans ta BDD. Et dans ton mail il suffit d'indiquer la chaîne en clair, tout simplement.


Ou alors tu joues les choses plus en sécurité en générant une clé plutôt qu'un mot de passe.

En gros au lieu de générer un nouveau mot de passe tu génères la chaîne que tu veux (elle peut être très compliquée, c'est pas plus mal) que tu enregistres dans un champ de ta BDD (champ dont il faudra vérifier le contenu à la connexion : quelqu'un ayant déjà demandé un reset ne devrait pas pouvoir se connecter).

Dans ton mail tu fourniras donc un lien vers une page qui ira chercher le couple email/clé dans la BDD pour afficher un formulaire permettant à l'utilisateur de déterminer son nouveau mot de passe (sans oublier de réinitialiser le champ de la clé au passage).

Bonjour, OK merci beaucoup, tu m'a bien aidé avec tes explications. OK je vais voir le sel. Voici le code qui marche (si ça peut servir à d'autres qui seront dans le meme pproblem que moi):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
$db = Connexion();      // conexion à bdd

function random($car) {
    $string = "";
    $chaine = "a0bc1de2fg3hi4jk5lm6no7pq8rs9tuvwxyz";
    srand((double)microtime()*1000000);
    for($i=0; $i<$car; $i++) {
        $string .= $chaine[rand()%strlen($chaine)];
    }
    return $string;
}

$erreur_loginIDp = NULL;
$info_loginIDp = NULL;

if(!empty($_POST['envoi_loginIDp'])) {

    $mail_id_perdus = htmlspecialchars(addslashes($_POST['mail_id_perdus']));

    if(!empty($mail_id_perdus)) {

            $requete = $db->prepare("SELECT * FROM utilisateurs WHERE mail = :mail LIMIT 1");
            $requete->execute(array('mail'=>$mail_id_perdus));

            if ($requete->fetchColumn() != 0) {             // si utilisateur existe


                $chaineRandom = random(20);             // mot de passe aléatoire

                // nouveau mot de passe aléatoire dans bdd
                $requete = $db->prepare("UPDATE utilisateurs SET mdp = :mdp WHERE mail = :mail LIMIT 1");
                $requete->execute(array('mdp'=>sha1($chaineRandom), 'mail'=>$mail_id_perdus));


                $info_loginIDp = '<span class="vert bold">Adresse mail OK</span>';

                $mail = 'admin@admin.admin';
                $destinataire = 'mail@live.fr';
                $sujet = 'ID perdus ?';
                $entete = 'From: '.$mail.''."\r\n";
                $entete .= 'Content-Type: text/html; charset="utf-8"'."\n";                     // ".=" pour continuer la ligne du dessus. "\n" : Pour retour à la ligne
                $entete .= 'Content-Transfer-Encoding: 8bit';
                $message = 'Vous avez demandé un nouveau mot de passe.<br />
                Voici votre nouveaux mot de passe : <span style="color:blue;">'.$chaineRandom.'</span><br />
                Si vous le souhaitez, vous pourrez le modifier dans la rubrique "utilisateur".';

                if(mail($destinataire, $sujet, $message, $entete)) {                             // si mail vaut true (si mail a été envoyé)
                    $info_loginIDp = '<span class="vert bold">Un message vous a été envoyé</span>';
                }
                else {
                    $erreur_loginIDp = '<span class="rouge bold">Une erreur est survenue</span>';
                }

            }
            else {
                $erreur_loginIDp = '<span class="rouge bold">Adresse mail incorect !</span>';
            }

            //$requete->closeCursor();

    }
    else {
        $erreur_loginIDp = '<span class="rouge bold">Veuillez remplit le champs</span>';
    }

}
?>
+0 -0
1
$mail_id_perdus = htmlspecialchars(addslashes($_POST['mail_id_perdus']));

Cette ligne est, si ce n'est inutile, plus dangereuse qu'autre chose.

  1. Tu utilises des requêtes préparées, tu n'as donc pas à échapper tes variables pour la requête
  2. htmlspecialchars s'utilise à l'affichage : ça protège des failles XSS, rien à voir avec la base de données donc.
  3. addslashes ne sert à rien si PHP est configuré correctement (notamment en désactivant les magic quotes). C'est une antiquité qui ne sert plus à grand chose dans la plupart des cas et qui de toute façon ne protège en rien des failles SQL

En passant, tu peux aussi corriger les messages d'erreur : il n'y a pas que le code qui peut contenir des fautes, la langue française t'en remerciera.

OK merci, les fautes d'orthographes c'est corrigé.

Par contre je ne voit pas en quoi…

1
$mail_id_perdus = htmlspecialchars(addslashes($_POST['mail_id_perdus']));

… cette ligne peut être dangereuse, (inutile oui je voit ce que tu veux dire étant donné que j'utilise une requête préparée, mais dangereuse je ne voit pas).

Pour le sel que tu me conseillais de mettre, c'est mieux que le salt ? On m'avais déconseillé le mdp5, on m'avais conseillé le sha1 avec un salt (perso je débute donc c'est pas facile tout les jours).

merci

La ligne est dangereuse parce qu'elle modifie ce qu'a entré l'utilisateur sans que tu y gagnes quoi que ce soit. Tu risque donc de créer des bugs pour rien.

Sel et Salt c'est la même chose dans deux langues différents. Après c'est à toi d'en créer un assez sécurisé. md5 est effectivement beaucoup trop simple maintenant : des dictionnaires existent et sont plutôt complets.

sha1 commence d'ailleurs à suivre la même voie, d'où l'intérêt du sel qui complique un peu les choses.

Bonsoir,

En fait ce que j'ai fait, ce n'est pas bon.

Car avec UPDATE ça modifie le mot de passe (donc l'ancien mot de passe n'est plu valide…). Et si c'est quelqu'un d'autre qui tapé l'email (dans la page mot de passe oublié), le vrai utilisateur ne pourra plu se connecter avec son ancien mot de passe.

La j'avoue, je suis bloquer.

Quelqu'un peut m'aider? Merci beaucoup

En même temps si tu envoie le nouveau par email, c'est pas bien grave que l'ancien ne fonctionne plus.

ça fait pas très pro comme boulot. Car si ce n'est pas un membre qui a demander à modifier le mot de passe, mais quelqu'un d'autre de malveillant. Le membre va être surprit en essayant de se connecter (si il n'a pas regardé sa boite mail).

Je vais voir la solution de la clé (j'avoue, je vois pas très bien ce que c'est exactement) merci

En même temps si tu envoie le nouveau par email, c'est pas bien grave que l'ancien ne fonctionne plus.

ça fait pas très pro comme boulot. Car si ce n'est pas un membre qui a demander à modifier le mot de passe, mais quelqu'un d'autre de malveillant. Le membre va être surprit en essayant de se connecter (si il n'a pas regardé sa boite mail).

stephweb

Au moins tu peux être sûr que l'utilisateur se rendra compte de la tentative d'attaque.

La clé est sur le même principe mais justement ne change pas le mot de passe : tu utilises un champ supplémentaire en base pour stocker la clé qui permet de vérifier que c'est bien l'utilisateur qui change le mot de passe.

OK merci pour les explications, ça ma bien aidé. Du coup ce matin j'y est fait avec la clé, et visiblement ça fonction bien. Voici le code (au cas où d'autres débutants auraient le même problème que moi).

_La page MDP perdu

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<?php
$db = Connexion();      // conexion à bdd

$erreur_loginIDp = NULL;
$info_loginIDp = NULL;

if(!empty($_POST['envoi_loginIDp'])) {

    //$mail_id_perdus = htmlspecialchars(addslashes($_POST['mail_id_perdus']));
    $mail_id_perdus = $_POST['mail_id_perdus'];

    if(!empty($mail_id_perdus)) {

            // requete SQL pour savoir si utilisateur existe
            $requete = $db->prepare("SELECT * FROM utilisateurs WHERE mail = :mail LIMIT 1");
            $requete->execute(array('mail'=>$mail_id_perdus));

            if ($requete->fetchColumn() != 0) {             // si utilisateur existe

                $cle_aleatoire = random(20);                // clé aléatoire que l'on cryptera dans la BDD

                // requete SQL pour envoye le mail avec la clé au bon utilisateur
                $requete = $db->prepare("SELECT * FROM utilisateurs WHERE mail = :mail LIMIT 1");
                $requete->execute(array('mail'=>$mail_id_perdus));
                $data = $requete->fetch();
                $mail_dest = $data['mail'];

                // requete SQL pour nouveau mot de passe aléatoire dans bdd
                $requete = $db->prepare("UPDATE utilisateurs SET cle = :lacle WHERE mail = :mail LIMIT 1");
                $requete->execute(array('lacle'=>sha1($cle_aleatoire), 'mail'=>$mail_id_perdus));

                $mail = 'admin@changer.mdp';
                $destinataire = $mail_dest;
                $sujet = 'ID perdus ?';
                $entete = 'From: '.$mail.''."\r\n";
                $entete .= 'Content-Type: text/html; charset="utf-8"'."\n";                     // ".=" pour continuer la ligne du dessus. "\n" : Pour retour à la ligne
                $entete .= 'Content-Transfer-Encoding: 8bit';
                $message = 'Vous nous avez signalé avoir perdu votre mot de passe.<br />
                Voici la clé à entrer : <span style="color:blue;">'.$cle_aleatoire.'</span><br />
                ici <a href="verification_cle.php">Cliquez ici</a>';

                if(mail($destinataire, $sujet, $message, $entete)) {                             // si mail vaut true (si mail a été envoyé)
                    $info_loginIDp = '<span class="vert bold">Un message vous a été envoyé à votre adress mail</span>';
                }
                else {
                    $erreur_loginIDp = '<span class="rouge bold">Une erreur est survenue</span>';
                }

            }
            else {
                $erreur_loginIDp = '<span class="rouge bold">Adresse mail incorrect !</span>';
            }

            $requete->closeCursor();

    }
    else {
        $erreur_loginIDp = '<span class="rouge bold">Veuillez remplir le champs</span>';
    }

}
?>

_Et la page verification_cle:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<?php
$erreur_mdp = NULL;
$info_mdp = NULL;

if(!empty($_POST['submit_cle'])) {

    $cle = $_POST['input_cle'];
    $mdp1_user = $_POST['input1_mdp_user'];
    $mdp2_user = $_POST['input2_mdp_user'];

    if(!empty($cle) && !empty($mdp1_user) && !empty($mdp2_user)) {

        $cleS = sha1($cle);     // car la clé est crypté dans la BDD

        $mdp1_user = sha1($mdp1_user);
        $mdp2_user = sha1($mdp2_user);
        $mdp1_userS = ($salt.$mdp1_user);
        $mdp2_userS = ($salt.$mdp2_user);

        // requete SQL pour savoir si clé existe
        $requete_SE = $db->prepare("SELECT * FROM utilisateurs WHERE cle = :cle_entree LIMIT 1");
        $requete_SE->execute(array('cle_entree' =>$cleS));

        if ($requete_SE->fetchColumn() != 0) {              // si clé existe

            if($mdp1_userS==$mdp2_userS) {

                // requete SQL pour modifier mdp
                $requete_UP=$db->prepare("UPDATE utilisateurs SET mdp = :nouveau_mdp WHERE cle = :cle_entree LIMIT 1");
                $requete_UP->execute(array('nouveau_mdp'=>$mdp1_userS, 'cle_entree'=>$cleS));

                $info_mdp = '<span class="vert bold">Le mot de passe a bien été modifié.</span>';

                // requete SQL pour supprimer clé, après que mdp soit changé
                $requete_UP = $db->prepare("UPDATE utilisateurs SET cle = :lacle WHERE cle = :cle_entree LIMIT 1");
                $requete_UP->execute(array('lacle'=>NULL, 'cle_entree'=>$cleS));
                $requete_UP->closeCursor();

            }
            else {
                $erreur_mdp = '<span class="rouge bold">Le mot de passe doit être identique dans les deux champs.</span>';
            }

        }
        else {
            $erreur_mdp = '<span class="rouge bold">La clé entrée n\'existe pas !</span>';
        }

    }
   else {
        $erreur_mdp = '<span class="rouge bold">Veuillez entrer tout les champs</span>';
    }

}   // END if(!empty($_POST['submit_cle']))
?>

<form action="verification_cle.php" method="POST">
    <label class="ancien-mdp" for="id_cle_user">Entrez la clé reçu par mail :</label>
    <input class="input-text" type="text" name="input_cle" id="id_cle_user" required><br>

    <label class="nouv-mdp" for="id_mdp1_user">Nouveau mot de passe :</label>
    <input class="input-passwd" type="password" name="input1_mdp_user" id="id_mdp1_user" required><br>

    <label class="confirm-mdp" for="id_mdp2_user">Confirmer le mot de passe :</label>
    <input class="input-passwd" type="password" name="input2_mdp_user" id="id_mdp2_user" required>

    <input type="submit" name="submit_cle" value="Envoyer">

        <?php echo $info_mdp; ?>
        <?php echo $erreur_mdp; ?>
</form>

Tu devrais aussi vérifier que la clé correspond bien à l'email. Histoire de limiter un peu les risques…

Un petit AND email = :email ligne 21 de ta page de vérification (sans oublier d'ajuster le formulaire) et le tour est joué. ;)

D'ailleurs tu devrais fournir un lien direct dans l'email pour éviter à tes utilisateurs d'entrer les paramètres tels que la clé et l'adresse email. C'est du temps de gagné et des champs en moins à traiter (qui peuvent d'ailleurs devenir invisibles).

Ok merci des conseils, sympa.

En fait l'utilisateur reçoit une clé dans sa boite mail, (et seul lui a accès à la clé, et la clé est crypté dans la BDD). Dans la page de vérification de la clé, il a juste la clé à taper et son nouveaux mot de passe (il n'a pas son mail à taper donc je ne voit pas en quoi AND email = :email serait utile). mais j'y modifierai peut erte plus tard.

En fait ce serait une sécurité supplémentaire. En cas d'attaque bruteforce, plutôt que de n'avoir que la clé à deviner, il faudra avoir le couple clé/email.

Ça peut paraître bête, mais comme c'est une info que tu peux demander sans que l'utilisateur n'ait à taper quoi que ce soit (comme tu peux le mettre dans l'URL ou dans un champ caché pour lui), c'est toujours bon à prendre.

à ok. La clé est créé que lorsque l'utilisateur à cliqué sur "mot de passe oublié", et une fois que l'utilisateur a changé son mot de passe dans la page de vérification la clé est détruite de la bdd. Je pensais que ça suffisais, mais je vais faire comme tu m'a dit, on sais jamais. merci

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte