How to implement “forgot password” functionality?

Use HTTPS only for this, and then we'll get onto details of implementation.

First of all you're going to want to protect against user enumeration.

That is, don't reveal in the page whether the user exists or not. This should only be disclosed in the email itself.

Secondly you will want to avoid referrer leakage. Ensure no external links or external resources are present on your target link. One way to mitigate this is to redirect after the initial link is followed so that the token is no longer in the query string. Be aware that if something goes wrong (e.g. your database is briefly down), and a standard error template is shown, then this may cause the token to be leaked should it contain any external references.

Generate a token with 128bits of entropy with a CSPRNG. Store this server-side using SHA-2 to prevent any data leakage vulnerabilities on your reset table from allowing an attacker to reset passwords. Note that salt is not needed. Expire these tokens after a short time (e.g. a few hours).

As well as providing the non-hashed token to the user in the email link, give the option for the user to navigate to the page manually and then paste in the token value. This can prevent query string values from being logged in browser history, and within any proxy and server logs by default.

Optionally you may want to make sure that the session identifier for the session that initiated the password reset is the one that followed the link. This can help protect an account where the attacker has access to the user's email (e.g. setting up a quick forward all rule with limited access to the email account). However, all this really does is prevent an attacker from opportunistically following a user requested reset - the attacker could still request their own reset token for the victim should they want to target your particular site.

Once the password has been reset to one of the user's choice, you will need to expire the token and send the user an email to let them know this has happened just in case an attacker has somehow managed to reset their password (without necessarily having control of their mail account) - defence in depth. You should also rotate the current session identifier and expire any others for this user account. This negates the need of the user having to log in again, whilst also clearing any sessions ridden by an attacker, although some users like to have the site log them out so they get a comfort blanket of confirmation that their password has actually been reset. Redirecting to the login page also gives some password managers the chance to save the login URL and the new password, although many already detect the new password from the reset page.

You may also wish to consider other out of band options for the reset mechanism and the change notifications. For example, by SMS or mobile phone alerts.


Your line of thinking is on the right track. However, I would suggest describing the flow for your forgotten password functionality from one step earlier. Somebody claims to have forgotten their password, you need to make sure you identify that this person is indeed the owner of the account for which you will start the password recovery procedure.

The flow below assumes a service where people can sign themselves up, such as a website (in other words, a list of registered emails and/or usernames can be built using the sign up functionality).

step one: identify the user

  1. Upon clicking the 'Oops, I forgot my password' link, present the user with a form where they enter their username and email address (optionally give them a hint about the email address in the form of: '[email protected]');
  2. inform them if they email address they entered belongs to the username they entered (yes or no);
  3. If the entered username and password match, continue with next steps.

step two: start recovery procedure

  1. generate a random recovery token (128 bits should be fine);
  2. store a hash of this token (created using a password hashing algorithm), along with its creation timestamp in your database;
  3. send the user an email with a link (containing the recovery token) to a change password form. The URL for the change password form should be HTTPS.
  4. when you receive the change password request, you check: [1]. the validity of the token (using its creation time compared to the current time, typically a 10 minute time window should be enough); [2]. if the token presented is correct (hash the token, compare the resulting hash to the one in your database, just as you would for a password);
  5. if all checks pass, continue with the next step.

Step three: update their password

  1. destroy all 'remember me' information you may have stored for this user;
  2. destroy any and all active sessions associated with this user;
  3. update the user's password;
  4. optionally send an email to their email address notifying them of the change;
  5. log the user in.

You are close to the best practice with your approach. A small addition would be:

  • You may require for the user name to match the token rather than just deducting the user from the token.

  • You should rather use https than http.

The other questions:

  • As to the generation: A usual way of generating those is using UUIDs, but as comments point out, you should rather use a completely random, sufficiently long string.

  • As to the deletion: You may clean up old, unused tokens regularly as you suggested. Maybe use a time frame that allows the mail get through usual email graylisting, e.g. 15 minutes.