How to Password Reset

A lot of companies and organisations does not do password resets properly today. Here is a recipe on how to do it securely.

  1. User enters the login page. This must be loaded over HTTPS.
  2. User clicks the “Forgot password” button. The user must then supply something unique to the user, e.g. email or username.
  3. The backend system affirms the details submitted, but does not give away if the details are correct or not. The system simply states that “A password reset email has been sent to the user, if it exists”.
  4. The system sends an email to the email address behind the username . The email must contain the following:
    • Who ordered the email? IP address and the country behind that IP is useful information.
    • Time and date the reset was ordered.
    • Some information regarding the password reset function and a notification to ignore this email, if it was not them who ordered it.
    • A unique link back to the system where the password reset in itself is done. The link needs to have the following properties:
      • It should contain a high entropy unique key, e.g. a long and strong unique key. This key should be as good as impossible to guess.
      • A fixed time the key is valid, e.g. the link only allows password resret if it is clicked within 15-30 minutes.
      • The link must be loaded over HTTPS.
    • Inform on the duration the link is active and that it is a one-time use link.
  5. The user is then taken to a form where he can enter his new password.
  6. Send a new email to the user, notifying that his password was in fact changed.
  • One question not answered here, is what you do wrt. users who enter an email address, believing it is a valid one, but which is not.

    a) Do you simply ignore these requests (implicitly suggested here?); or
    b) Do you send an email to this recipient, stating that an attempt has been made on this site to reset the password, but an account using this email address was not found, including the details you specify in 4, just except for that last one which can be replaced by a link to “create an account” (if feeling persuasive), and/or a link to “forgot your username?”, if one such function exists.

    If b) is chosen, the email text you propose in 3 could then be altered to something like “An email has been sent with instructions on how to proceed” (of course, still not giving away details on if the account exists or not)? While I don’t recall any hard numbers on how many users forget their username as well, I believe these adjustments could improve the user experience further, without compromising security.

    I also believe the email in 3 always should contain a notice on the limited temporal validity of the link, and that it can only be used once. From sites I have stats on, it appears that some people tend to use this link repeatedly, possibly approaching login by searching for the last email received from the site and then just hitting the link without even trying to remember the password. Then, the question arises if the site should just give a 404, or rather let the user know:
    c) whether or not the link was valid at some point in time before, and that it has expired
    d) which username the link used to be valid for, and simply prompt for the password in a login form straight on

    If, as you say, the unique reset key is long and strong enough (i.e. unguessable), would c) or even d) be a security issue?

    • I would not send any emails, thus A. Of course this script could be used to verify usernames or emails in the system, so a sort of rate limiting, captcha or max amount of tries, should be enforced.

      Option B would allow any unauthenticated user to send emails to any arbitary email address. There is someting unpleasant with this 😉 e.g. it could be abused, either by single users or a mischevious botnet wanting to spam someone. It could potentially leave your domain blacklisted. So why bother with this risk?

      The user must know either their username or email. If that is impossible, please contact support imo. Option B just overcomplicates things.

      I agree with the content of step 3, it should contain information of its one-time use and validity. Added this to the post 🙂 Thanks!