A better reset password system

When building a website with sign up / login, we have to build a feature that allow user to reset his password if he forgot it. There are many technical ways could do this. But I am gonna show you a better way to do this in elegant user experience and less code lines.

USER EXPERIENCE

  • User forget his password, and clicked password reset link
  • Show user a form to let him input his email address
  • User get an Email in his inbox containing a reset password link ( link will expire in 1 hour )
  • User go to that link, enter new password for his account

Regarding user experience, it is nothing new because most websites do it like this.

HOW TO DESIGN BACK-END

  • After user enter his email address in reset password form, validate this email in database. If valid, get the user _id
  • Encrypt the userid with timestamp and a secret string ( let's call the secret string KEY ) to get the HASH string. for example: HASH = MD5 ( userid + timestamp + KEY )
  • Send an Email to user's email address containing a reset password URL with HASH parameter and other needed information. for example: http://domain.com/reset-password.php?hash=HASH&user_id=123&timestamp=1392121211
  • When user arrives the reset password page, we get the hash parameter, userid and timestamp. Now, we need to verify the parameters by checking if HASH == MD5 ( userid + timestamp + KEY), which KEY is a pre-defined value in back-end server
  • Then, we need to check the timestamp to determine if this link is expired
  • If all validation passed, show user the reset password form

In this new way:

  • we do not need any new database tables
  • parameters in reset password link are safe, if user change the user_id or timestamp, he will not pass the validation
  • reset password link contains timestamp to verify expiring
  • it is safe enough if we use a long string for KEY, because MD5 is a one-way encryption

Here are some code to help you understand the idea better:

send-reset-email.php:

$KEY = "something really long long long long long and secret";
$email = $_POST['email'];
$user = get_user_by_email($email);
if ($user && $user['id'])
{
    $time = time();
    $hash = md5( $user['id'] . $time . $KEY);
    $url = "http://domain.com/reset-password-form.php?id=".$user['id'].'&timestamp='.$time.'&hash='.$hash;
    send_email($email, 'reset password email from xxx.com', ' Please click the following link to reset password'. $url);
}

reset-password-form.php:

$KEY = "something really long long long long long and secret";
$hash = $_GET['hash'];
$user_id = $_GET['id'];
$timestamp = $_GET['timestamp'];

if ($hash == md5( $user_id . $timestamp . $KEY ))
{
    if ( time() - $timestamp > 3600 ) // one hour
    {
        die('link expired');
    }
}
else
{
    die('invalid parameters');
}

//validation passed

if ($_POST['new_password'])
{
    reset_user_password($user_id, $_POST['new_password']);
    die(' password changed successfully ');
}
else
{
    echo '
        <form action="reset-password-form.php?hash=$hash&id=$user_id&timestamp=$timestamp" method="post">
            new password: <input type="password" name="new_password">

            <input type="submit" value="submit">
        </form>
    ';
}

The code above is not runnable, it's just an idea to help you understand better. Hope you can enjoy it :)

-- END --