Why to Set a Time Limit on Password Reset Emails

You know those password reset links that are sent to you get when you forget your password? Well, some of them set a limit on how long you can use it before the link stops working. For the life of me, I couldn’t figure out why sites did this. Who cares how long it takes me to get around to resetting my password? Why not just make the same link work every time a person wants to reset his or her password?

So, I coded up a registration and password reset system for Domain Pigeon without setting a time limit on reset password links.

Last night, somewhat randomly, it hit me why this is a bad idea. It’s so obvious now that I don’t know why I didn’t think of it sooner.

If you reset your password in a public place, such as a library computer, the reset password URL will probably be stored in the browser navigation history. The next person who uses the computer might accidentally come across the “www.whatever.com/reset/…” URL and click it to see what happens. Surprise: it still works.

So how do you prevent this? You guessed it: a time limit.

Here’s how I implemented it for Domain Pigeon. When the customer requests a password reset email, store the time they requested it and then, to generate the URL, use a hash of the user’s email concatenated with the time they requested it. This’ll ensure that the URL is unique based on that specific request (aka a salt).

Then, when the customer clicks the link to reset his password, compare the current time to the time the link was sent and if it’s less than a specific amount of time, allow him to change his password. In pseudocode, this looks something like:

if hash(user.email + user.forgot_sent_at) = params[:hash] and user.forgot_sent_at + 2.hours > Time.now then
... yada yada yada
end

[Update: Note that the has function used here is a SHA-1 hash of the input concatenated with a secret key, so that the final product here = SHA(user.email + user.forgot_sent_at + long_random_string). Thank you to Artem for pointing out it needed to be clarified]

Lastly, after the password is changed, reset the stored time. That will prevent someone from changing the password twice using the same reset password link.

The only flaw I see with this method is for the person who clicks the link to reset his password and abandons it, because that’ll allow someone else to access the page and reset his password. Fortunately, this should be rare enough that it’s not a major problem. For extra security, set the time limit to five or ten minutes. After all, how many people request a reset password link and don’t access it within the next few minutes? For the few that do, they probably won’t mind the small annoyance in return for the extra security.

If anyone has any thoughts on this method or password reset algorithms in particular, please let me know.

6 thoughts on “Why to Set a Time Limit on Password Reset Emails

  1. I believe your salting method is flawed. An attacker performing a password reset on an account they do not own will know the approximate time the request was made. The attacker can then hash the victim’s email with a range of times corresponding to when the reset request was made. One of these would be the reset URL.

    For the salt you should instead generate a random value long enough not to be easily guessed. I am not sure exactly how you chose to implement the password reset, but after the value is used for hash generation, it could very well be discarded to prevent its inadvertent leakage.

    As a side note, I always thought that password resets were time-limited to prevent the database table holding reset information from growing indefinitely, and to ensure attackers don’t have a large amount of time to try various hash guessing attacks.

  2. I think I solved that problem, though I didn’t mention it here.

    My hash algorithm is basically the SHA-1 hash of the string concatenated with a third, secret key.

    So basically when I say hash(email time), it works out to be sha-1(email time long_random_string).

    I think that does it, no?

  3. Why not just make a password-reset page work only once? Have that long random url refer to a particular user, and let it work only for the first time the page is accessed. This seems to be the easiest way to make it secure.

  4. rudd, I think that would work too.

    I assume that if the user requests more than one password reset the older one gets deleted so that only one valid URL works at a time for that user?

    One minor thing you’d have to work out is if the user doesn’t enter the same password and password confirmation. Assuming he’ll be redirected to the same page with an error message, you’d have to make sure it doesn’t lock him out.

    Good thought though.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s