Jump to content
Tom8001

Forgotten Password

Recommended Posts

Hi, how can i create a Forgotten password script? I know security can be a real issue with this if the code isn't written correctly.

Share this post


Link to post
Share on other sites

One if the most important things is that passwords, rather, password hashes are properly created and stored in the DB as such. A forgot password is really a password reset since there is no way of knowing what the password is. Never ever store a password as plain text.

 

If you are current on your PHP version >=5.50 you can use password_hash and password-verify

 

Search this forum. Excellent answers to this question have already been posted.

Edited by benanamen
  • Like 1

Share this post


Link to post
Share on other sites

One if the most important things is that passwords, rather, password hashes are properly created and stored in the DB as such. A forgot password is really a password reset since there is no way of knowing what the password is. Never ever store a password as plain text.

 

If you are current on your PHP version >=5.50 you can use password_hash and password-verify

 

Search this forum. Excellent answers to this question have already been posted.

 

Thanks, I will take a look at other threads.

Share this post


Link to post
Share on other sites

I was thinking to just reset the password when the form is submitted and then query the database for the new password, decrypt it and send it via email?

Share this post


Link to post
Share on other sites

You cannot "decrypt" the password.

 

Read those other threads.

Share this post


Link to post
Share on other sites

You cannot “decrypt” a hash, because hashing is not encryption.

 

Actually, the whole point of hashing passwords is that it's practically impossible to retrieve the password (assuming it's sufficiently strong). If anybody could just turn the hash back into the password, the whole exercise would be rather useless, don't you thin?

 

So, no, you can't send people their old password. But you can reset the password:

  • Generate a sufficiently long random number (at least 16 bytes) using openssl_random_pseudo_bytes() or mcrypt_create_iv().
  • Hash the number with something like SHA-2 and store the hash in the database.
  • Send the number to the user by e-mail, embedded into a password reset link. For example
    https://www.yoursite.com/reset_password.php?token=01b81eb4247dd47bb554537db0c500a4
  • In the password reset script, you check if the random number is valid, and if it is, the user may change their password.
Edited by Jacques1
  • Like 1

Share this post


Link to post
Share on other sites

What i don't understand is when they request to reset their password, I will be hashing the password using password_hash but how am i ment to let them see the password in the email in plaint text?

 

Edit: Sorry we posted at the same time

Edited by Tom8001

Share this post


Link to post
Share on other sites

You don't let them see any plaintext password. Never send plaintext passwords.

 

Use a temporary random reset token as described above. With this token, the user can choose a new password.

  • Like 3

Share this post


Link to post
Share on other sites

Sorry i'm still fairly new to PHP i don't understand what to do about the token am i ment to use the rand() function?

Share this post


Link to post
Share on other sites

Hi, This is my forgot password code so far.

<?php

require('./includes/connect.php');

error_reporting(E_ALL | E_NOTICE);
ini_set('display_errors', 1);

if($_SERVER['REQUEST_METHOD'] == "POST")
{
 
    $email = $_POST['email'];
    $email = htmlentities($email, ENT_QUOTES);
    
    $stmt = $handler->prepare("SELECT email FROM users WHERE email = :email");
    $stmt->bindParam(':email', $email, PDO::PARAM_STR, 255);
    $stmt->execute();
    
    if($stmt)
    {
     
        $fetch = $stmt->fetch();
        
        if($email == $fetch['email'])
        {
            
            $stmt = $handler->prepare("SELECT username FROM users WHERE email = :email");
            $stmt->bindParam(':email', $email, PDO::PARAM_STR, 255);
            $stmt->execute();
            
            $row = $stmt->fetch();
            
            $username = $row['username'];
            
            $token = mcrypt_create_iv(MCRYPT_RAND);
         
            $headers = "Password Reset";
            $body = "Hi, ".$username."!, You have recently requested to reset your password. ".PHP_EOL." \n
            If you did not make this request please forget this email. ".PHP_EOL." To reset your password please click this link:
            <a href='http://ps3modding.co.uk/forgot_password.php?token=$token'></a>";
            
        } else {
         
            echo "The E-Mail Address entered was Not Found.";
            
        }
        
    }
    
}

?>

What i am wondering is because your not ment to store the token in the database how do you check to see if it is valid? Is it done by $_COOKIE?, 

 

Thanks

Share this post


Link to post
Share on other sites

Who told you not to store the token (Hash) in the database? What are you going to compare the users token to?

Edited by benanamen

Share this post


Link to post
Share on other sites

I read it on a stack overflow thread somewhere, And i don't know the token is what doesn't make sense to me.

Share this post


Link to post
Share on other sites

You're not supposed to store the plaintext token, but you do store its SHA-256 hash. To check if a token is valid, you hash it and search that hash in the database.

 

Generating a token:

<?php

// generate 16 random bytes
$rawToken = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);

// encode the random bytes and send the result to the user
$encodedToken = bin2hex($rawToken);

// hash the random bytes and store this hash in the database
$tokenHash = hash('sha256', $rawToken);

Looking up a token:

<?php

$encodedToken = $_GET['token'];

// decode the token and hash it
$rawToken = hex2bin($encodedToken);
$tokenHash = hash('sha256', $rawToken);

// now look up $tokenHash

Share this post


Link to post
Share on other sites
$encodedToken = $_GET['token'];

$token = hex2bin($encodedToken);
$tokenHash = hash('sha256', $token);

$username = $_GET['s'];

$stmt = $handler->prepare("UPDATE users SET reset = ".$tokenHash." WHERE username = :u");
$stmt->bindParam(':u', $username, PDO::PARAM_STR, 255);
$stmt->execute();

Fatal error: Call to a member function prepare() on a non-object

 

I get this error when clicking the reset link in the email, it says on line 10 which is the update query

Share this post


Link to post
Share on other sites

Your $handler variable is either not defined at all or pointing to something other than a PDO instance.

 

Also, don't insert $tokenHash directly into the query string. You already have a prepared statement, so use a second parameter for the hash.

Edited by Jacques1

Share this post


Link to post
Share on other sites

I don't see what you mean about not inserting the token hash in the query string?

Edited by Tom8001

Share this post


Link to post
Share on other sites

I mean this:

UPDATE users SET reset = ".$tokenHash." WHERE username = :u
                         ^^^^^^^^^^^^^^

You should do this:

UPDATE users SET reset = :hash WHERE username = :u
                         ^^^^^

Share this post


Link to post
Share on other sites

Thanks, the password is able to be reset now, but i have a field in the database called 'hash' and i have the query to update it with the hashed token but it does not change, 02842db41742292d76da3a9fb6f7e4ee.png

 

Here is my new updated code:

<?php

require('./includes/connect.php');

$encodedToken = $_GET['token'];

$token = hex2bin($encodedToken);
$tokenHash = hash('sha256', $rawToken);

$username = $_GET['s'];

$stmt = $handler->prepare("UPDATE users SET hash = :hash WHERE username = :u");
$stmt->bindParam(':u', $username, PDO::PARAM_STR, 255);
$stmt->bindParam(':hash', $tokenHash, PDO::PARAM_STR, 255);
$stmt->execute();

if($stmt)
{
 
    echo '
    
    <form action="" method="POST">
    
    <h3>New Password: </h3> <input type="password" name="newpass" placeholder="New Password" required /><br>
    <h3>Confirm Password: </h3> <input type="password" name="confpass" placeholder="Confirm Password" required /><br>
    <input type="submit" name="update" value="Update Password">
    
    </form>
    
    ';
    
} else {
 
    echo "Invalid token";
    
    exit;
}

if($_POST['update'])
{
    
    $newpass = $_POST['newpass'];
    $confpass = $_POST['confpass'];
    
    if($confpass == $newpass) {
        
        $enc_password = password_hash($confpass, PASSWORD_BCRYPT);
     
        $stmt = $handler->prepare("UPDATE users SET password = :cpass WHERE username = :u");
        $stmt->bindParam(':u', $username, PDO::PARAM_STR, 255);
        $stmt->bindParam(':cpass', $enc_password, PDO::PARAM_STR, 255);
        $stmt->execute();
        
        
        
        if($stmt) {
         
            echo "Your password has been reset!";
            
            echo '<meta http-equiv="refresh" content="0;login.php">';
            
        } else {
         
            echo "Error";
            
            exit;
            
        }
        
    }
    
}

?> 
Edited by Tom8001

Share this post


Link to post
Share on other sites

The code makes no sense and is actually dangerous, because you allow anybody to update anybody's password and take over their account. For example, I could request the following URL:

https://www.yoursite.com/reset_password.php?u=admin&token=idontcare

You would change the token of “admin” to “idontcare” (which is obviously nonsensical) and allow me to set a new password for the admin account. Now I'm an admin of your site.

 

It's really important that you understand the logic of a password reset before you write the code. Draw a diagram or write down a verbal description of the steps. Make sure you know what you're doing.

 

I'll repeat the steps again:

  1. A visitor requests a password reset token for a particular user account. At that point, the visitor cannot change the password, because they first have to prove that they actually own the account.
  2. You generate a random token, send it to the e-mail address of the account and store a hash of the token in the database.
  3. The visitor clicks on the password reset link in e-mail. This automatically submits the token to the actual password reset page.
  4. On the password reset page, you hash the token and check if that hash is present in your database. If it is, then the visitor may change the password.

So there are two steps: You generate a secret (the token) and send it to the user's e-mail account. The user can then use this secret to prove their identity and change their password. You should make two separate PHP scripts for the two steps.

 

OK? If you're still not sure how it works, simply try out the password reset on Facebook, Amazon or whatever site you're using.

Edited by Jacques1

Share this post


Link to post
Share on other sites

You're not supposed to store the plaintext token, but you do store its SHA-256 hash.

Why hash it instead of just using bin2hex()?

Share this post


Link to post
Share on other sites

For starters because you can use hex2bin() to un-hex it. You might as well just be storing plain text, not to mention, it doesn't encrypt anything.

Edited by benanamen

Share this post


Link to post
Share on other sites

Of course, but why does that matter? You're storing the same length value of the same entropy whether you hash it or not. So what is the point of hashing it?

 

EDIT: I guess part of my question is, why not just store it plaintext?

Edited by scootstah

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.