Password Resets: You’re Doing it Wrong

Your users will forget their password. Maybe not all of your users, but some of them will and when they do, you need a way to still let them access your site and their data. There are several ways this can be done; however, some common methods are simply not secure.

The easiest “I forgot my password” option is to just e-mail the user their password. They enter their e-mail address and the site pulls the password out of the database then sends it in an e-mail. The major problem with this method is that it relies on the password being stored as plain text. A major no-no!

You could encrypt the user’s password in the database to mitigate the plain-text problem but the accepted best practice is to hash passwords for storage.  A hashed password, however, can’t be decrypted which means you can’t send it to the user in an e-mail.

This leads us to the second and, probably most common, method for helping a user find their way back into your site: resetting their password for them.  A strong password is generated by the site, hashed and stored in the database, then e-mailed to the user. Hopefully, as a bonus, the user can change the password later to something they will more easily remember (like “password.”)

I’ve used this method for years but it has one glaring weakness: E-mail is not secure. There are too many failure points in the chain of events required to send an e-mail from a web application to the user’s inbox.  Not to mention, their inbox can, of course, be viewed by anyone with access to the user’s machine. So whether you’re sending the user’s original password, a newly generated password, or even a special token with instructions for manually changing their password, you simply can not assume that the person generating the “I forgot my password” request is, in fact, the legitimate user.

So how can you authenticate the user?

If you had unique, hard, data about them before they signed up, you could challenge against that.  Account number, last four digits of a social security number, date of birth, etc. The more hard data the better. With this information, coupled with answers to previously set up challenge questions (which I’ll talk more about below) you could simply re-register the user, linking them to their existing account. In other words, if you had a rigorous signup process and trusted the user when they first created their account, then you could replicate that procedure and trust them again.

While cumbersome for the user, this may work for a bank or insurance company’s website where you are linking a web user to an existing data set. Most sites, however, know very little about their users before they sign up and, more than likely, only need a unique identifier (like an e-mail address) and password to grant them access.

The answer, then, lies in two-factor user authentication. In truth, if you initially verified the user’s e-mail address when they registered, you are already using two factors. The password is something the user knows and their verified e-mail address is something the user has. The issue we’re addressing here is, what happens if the user forgets the “something known,” i.e., their password.

This can be solved by introducing additional knowledge-based security parameters.  These could be data that only the user would know, gleaned from their existing account or, more commonly, the now ubiquitous challenge questions such as “What was your first pet’s name?” Challenge questions alone are by no means foolproof. A little research on a user could easily dig up their school mascot and first employer. In fact, these questions, while commonplace, are not FDIC compliant for banking purposes.

That being said, and recognizing the potential weakness of challenge questions, it is important that simply answering the questions is not a sufficient enough action to replace the password mechanism. This is where we fall back on the e-mail address for authentication. E-mail communication, as previously discussed, is vulnerable, just as is the challenge question system. In combination, though, they mitigate each other’s weaknesses.  That is to say, someone might be able to answer your user’s security challenge question but does not have access to their e-mail account. Likewise, they may be able to intercept a user’s e-mail but would not be able to generate that e-mail in the first place because they do not know the challenge answers.

The addition of challenge questions is a backup for forgetting the knowledge factor in the two-factor authentication, but what about the other factor, the e-mail address?  If your user’s address has changed since they registered or they are aware that it has been compromised, you’ll need a backup method for this second-tier of authentication as well. At registration, you could ask for, and verify, a second e-mail address – although many users only have one address or may be reluctant to give out such an address. Since the basis of the second authentication point is “something the user has” you could get a little creative. Perhaps mail your user a password reset token through the postal service to their verified street address. That process might be a little slow. There’s a good chance your user has a cell phone and can receive text messages; this seems like a reasonable hardware authentication method to me!

So here is my preferred security flow:


1) Verify a user’s e-mail address.

Upon registration and password creation, and preferably before granting access to the site, send the user an e-mail to the address they provided that includes a token value assigned to their newly created account. To verify their account they must return to the site and enter that token value.

2) Store security challenge answers

Once a user has registered and you have verified their e-mail address, ask them to answer several pre-defined challenge questions. These should be predefined to avoid easily guessed answers or those susceptible to brute force, such as “In what state were you born?” which could be guessed in less than 50 attempts. These answers should be stored and treated just like the password value: hashed and salted so they can not be found or reversed by someone with database access.  Also, be sure to normalize the answer before hashing, perhaps stripping whitespace and making it all lowercase, making re-entry a little more forgiving.

3) Optionally verify your user’s cell phone number

Ask the verified user to enter their phone number and then text them a token value which they must enter on the site to verify that they have physical access to the device.

Password Recovery

1) The user indicates they do not know their password but can provide their public access identity (e.g., their username or email address.)

2) The user answers at least two of their previously determined security challenge answers.  Alternatively, they could also be prompted to enter a token code that you send via SMS text message to their verified phone number.  Although, not to over think the issue, but if your user’s cell phone has been compromised, the imposter could very well also have access to the user’s e-mail through the phone.

3) Have passed this first authentication step, the user is prompted to create a new password. This password does not yet let them into the site until you have been able to verify their second authentication factor in the next steps.

4) Send the user an e-mail or text message (their choice) with a token value.

5) When the user receives the message and enters the token value into the site, thus proving they have access to the hardware device, they are then prompted to re-enter the new password they created in step 3. This ensures that the user entering the token was the user who initiated the request and not someone who intercepted the e-mail or text message.

6) At this point, the user has been fully authenticated and can gain access to the site. On the backend, the newly created password replaces the old password for future logins.

A more thorough walk-through of this process, including the required database tables structure can be found in this google doc. There, I outline some additional tracking steps and speed bumps that should be implemented to throttle brute force attacks as well as how to store tokens and define how to undo a password reset.