PHP – Modern Recommendations for Password Recovery

authenticationpasswordsPHPSecurity

I'm going to implements password recovery in my authentication. I haven't put this together in a while and wondering if there is anything I ought to be aware of.

My idea at the moment is:

  1. User clicks "Forgot my password" to go the password recover page: a
    form with an email field
  2. They enter their email, and an email is sent to that address with a
    link and password recover token/key (MD5 string – it just needs to
    be somewhat random and long right?). An entry is also made in the
    password_recovery database table which ties that token to their
    account, and an expire date (1 hour?)
  3. They retrieve the email and click the link to take them to a
    password set page: two fields to enter their password, and confirm
    their password again.
  4. Done, please login again with new password

Does that seem OK? Anything changed over the years where this approach is no longer recommended?

UPDATE

Additions that I opted for:

  • I store the token in the database hashed. If a hacker we to be able to access the database table somehow, they wouldn't be able to use the stored tokens .. hopefully (hashed with sha256)

Best Answer

Does that seem OK?

Yes, assuming emails are the primary form of authentication of your site.

Anything changed over the years where this approach is no longer recommended?

Not that I know of.

Here are some points that you need to take into consideration.

What type of feedback you will give the user

When the email does not exist, what will you say and do?

  • If you display a message like "Sorry, this email doesn't exist", you are effectively leaking information about who is or isn't in your database.
  • So instead, you can display a message along the lines of "If this email corresponds to a user account, you will get further instructions". This is more secure, but it might confuse your users ("why didn't I get the email?").
  • A third option is to always send an email, only with a different content if the email wasn't in your database. This opens up your form for abuse though, as anybody could type anybody else's email, and your emails would eventually be spammed.

If your system uses username, it might be better to ask for the username, not the email address. You send the recovery email to email address associated with the account, without disclosing it to the user.

How do you protect the process against abuse

  • It's easy to see how somebody can use this form to generate emails, and then use social engineering to incite a user to click the link (or worse, forward the email). Therefore, you should make it very clear in your email.
  • You need to protect this form against brute force one way or another. Some examples: no more than one active request per account (do no resend emails until the previous one expires), limited number of tries per IP addresses, throttling, CAPTCHA, etc.

The token

The token is really a One Time Password for a user account.

  • The token should be random and unpredictable, and stored in the database with an association to the corresponding user ID, and the timestamp. You do no need MD5, nor do you need to encode anything in the token. Example in PHP, for a 32 character token: bin2hex(openssl_random_pseudo_bytes(16));
  • The token must have a short life time. 1 hour seems a bit long for me, I assume if you forgot your password you want to recover it now, so 30 minutes is probably plenty, but honestly this is up to you. The point is to limit the token lifetime.
  • You must disable the token as soon as it has been used.
  • You should not have more than one active token per user at any given time. From an relational database point of view, it's easier to enforce this rule when using a (nullable) field in your users table, rather than a separate table.
  • I'm unsure as to the usefulness of hashing the reset token. From your database, the risk is already mitigated by the short lifetime, and one-time-only usage. The real risk is in the transport, as they are sent by email (unsecure), so they can be compromised then. But why not.
Related Topic