47

Passwords are hashed so that if someone gains access to a database of passwords then they won't know what the actual passwords are and so they can't log in.

If I can get a valid password reset token however (the kind which would be emailed to a user when they've forgotten their password) then isn't this as good as a password?

I could take a token, plug it into the reset page and set the password to whatever I want and now I have access.

Thus shouldn't password reset tokens also be hashed in the database?

5
  • 1
    It is not as important as for the passwords. But the additional cost of storing a salted hash of the token rather than the token itself is likely so low, that it would be silly not to hash it.
    – kasperd
    Apr 26, 2015 at 17:05
  • 2
    Why do you even need to store them in the database in first place? In all my software, we create reset tokens from the last login time, password hash and email address and a private key. When the reset url is called, get the hash again and compare.
    – AKS
    Apr 27, 2015 at 3:01
  • 4
    @AyeshK: For greater security you would need your tokens to expire. You should store an expiry time for each reset link and add this to the hash. Apr 27, 2015 at 10:11
  • @SilverlightFox Store the expiry time where? Apr 27, 2015 at 11:12
  • 2
    @IanWarburton: You could store it in the link itself as it is validated by the hash, assuming the hash mechanism is secure and not vulnerable to any length extension attacks. Apr 27, 2015 at 12:11

3 Answers 3

31

Yes, you should hash password reset tokens, exactly for the reasons you mentioned.

But no, it's not quite as bad as unhashed passwords, because

  1. reset tokens expire and not every user has an active one
  2. users notice when their passwords are changed, but not when their passwords are cracked, and can thus take steps to limit the damage (change password and other sensitive data, etc).

Additionally, as users reuse passwords, an attacker can try a cracked passwords for other accounts, such as the users email, thus increasing the damage.

7
  • 7
    @IanWarburton - If your token has enough entropy, lets say 20 random characters 0-9 a-z A-Z, then you can calculate an unsalted fast hash (e.g. SHA-256 or SHA-512) and store it. This is safe, because it is not possible to successfully brute-force such strong "passwords". Salting is done, because passwords choosen by people are often relatively weak, because they have to be remembered. Apr 26, 2015 at 20:13
  • @martinstoeckli I think salting is done to make it harder to find the hashing key. For example if you can find which string results in a particular unsalted hash then you have an easier time of deducing the key. Apr 27, 2015 at 0:38
  • @IanWarburton - No, hashing is a one-way operation and doesn't need a secret key. You probably think of an encryption algorithm which needs a key to decrypt the string. You could have a look at my tutorial about safely storing passwords to get more in-depth information. Apr 27, 2015 at 6:26
  • @martinstoeckli Oh I didn't realise that hash functions always produce the same value without a salt. I thought they'd always require at least some random input. Well even so, the facebook code for example is only a six digit number - so a trivial one million possible values. Apr 27, 2015 at 10:41
  • @IanWarburton - A six digit number has not nearly enough entropy to be used as a token, so it is not safe to store its hash in the database. A salt won't help neither, because the salt is known and a million possibilities can be brute-forced in a fraction of a second. If you take the code as a password, you would have to use a slow salted key-derivation function at least. Apr 27, 2015 at 11:24
23

No, there is no overwhelming need to hash password reset tokens, as long as they are time-limited and single-use. There's some benefit to hashing reset tokens, but the benefit is less than with passwords, so I wouldn't consider hashing of reset tokens absolutely necessary.

Typically, password reset tokens are time-limited. For instance, they might be good only for one hour, and they expire after that. Also, normally password reset tokens are limited so they can only be used once, and after being used they are revoked. This is good practice, and you should definitely be doing this.

If you do this, there's no absolute need to hash the password reset tokens when they're stored in your database. Let's remember the threat model that hashing is designed to defend against: it's designed to mitigate one-time database breaches. In other words, we assume that the adversary finds a vulnerability that lets him dump the database at a single point in time. (Maybe a SQL injection vulnerability, or something like that.) We want to limit the damage as much as possible, if this happens.

For passwords, it's important to hash them. If you don't, two bad things happen: (i) a single database breach compromises every user's password, allowing the attacker access to every user's account on your site; and (ii) because users often reuse their passwords on other sites, this may allow the attacker access to your users' accounts on other sites. If you do hash, a database breach is still bad, but it's much less bad.

For password reset tokens, neither of those is true. Let's say you don't hash the password reset tokens. Then a single database breach will only reveal the set of reset tokens that are active at the time of the breach. You might have millions of users on your site, but only a few reset tokens active at that tiem, so only a few users have their accounts exposed. That's much, much less serious.

Also, password reset tokens are chosen randomly, not by the user. As a result, they're not re-used on other sites, and compromise of a reset token for site x.com doesn't pose any risk to the user's account on other sites (e.g., y.com). Thus, the second reason why we hash user passwords doesn't apply to reset tokens, either.

It's worth mentioning there is one scenario where unhashed password reset tokens can be exploited: if the attacker manages to gain read access to the database, the attacker can requests a password reset for some user (alice), read the database to observe alice's password reset token, and then use it to change alice's password and gain access to her account. However, there are some mitigating factors here. The attacker has to do this in real-time, so the attacker's time window is limited. The attack is highly noisy and detectable, since an email gets sent to alice. If the attacker tried to take control of many accounts in this way, the breach would probably be noticed very quickly (users would contact the site's admins, who would hopefully investigate). This is a deterrent to attackers trying to mount such an attack against many users. It doesn't stop a targeted attack where the attacker only wants to affect a few users, but it does make it less likely that an attacker will be able to gain access to everyone's accounts (like would happen with unhashed password). So, unhashed reset tokens are less worrisome than unhashed passwords.

Don't get me wrong. There is some benefit to hashing password reset tokens. However, it also takes some effort to write the code to hash the reset tokens. Since the security benefit is limited, from a risk management perspective it would not be ridiculous to decide to store reset tokens un-hashed.

I'm not saying "don't hash reset tokens". If you have a chance to hash them, go right ahead: you will get some security benefit. At the same time, to put this into perspective, this probably isn't the most important security feature to implement. You can probably find many other ways to harden the security of your site that will offer greater benefits.

Bottom line: Hashing password reset tokens has some benefit, but it's limited. I don't consider it a must-have security feature.

7
  • 5
    If an attacker has read access to the database (SQL-injection), he could request a reset for any account he wants, even for admin accounts. Because he can see the new generated token, he could take over this account. Apr 27, 2015 at 6:21
  • @martinstoeckli, thank you for mentioning that scenario. I've edited my answer to provide extensive discussion of that scenario. Yes, you're right to mention that scenario -- it can happen -- though there are some mitigating factors that I think make it significantly less serious than the corresponding attack on passwords. I don't think it changes the bottom-line conclusion much.
    – D.W.
    Apr 27, 2015 at 6:50
  • 5
    "… so only a few users have their accounts exposed." this is enough to hash tokens. Isn't it? Mar 2, 2016 at 10:17
  • 3
    @alexw - While this is of course true, the problem remains for all other accounts, and calculating a hash is really no problem (no need for salting and key-stretching). Nov 2, 2016 at 6:47
  • 3
    @PHPst I agree. If you can choose between having some (even just a few or even just one) accounts at risk or having zero accounts at risk without much extra work, why wouldn't you go for the latter option? I think it's the responsible thing to do. Dec 16, 2016 at 3:26
-2

It takes time and effort to hash just a time expired token. In a mobile app, let's say User1 requests to reset password, the system will generate a temp 6 digits token + 15 mins expiration:

  • Case 1: The system saves the token directly to the DB, sends a link to User1's email with the token. User1 will enter the token in the reset password screen. And the workflow goes on...
  • Case 2: The system hashes the token and saves the hash to the DB, sends a link to User1's email with the hash string (system didn't save the token but the hash, it just can send the hash). How can User1 get the token to enter in the reset password screen in this case?

Of course you can design for User1 to enter the hash, not the token but the hash is not as user-friendly and not 6 digits. Entering a hash isn't user-friendly in a mobile app, is it?

Nowadays, most mobile apps do the password reset by sending a short digits token/authentication code, allow users to log into the system to reset the password.

1
  • 1
    Your Case 2 has a few things mixed up. If the point is to protect the "string" stored on the server so that a break of the server does not affect the "string", then using the hash of the string doesn't do anyone any good. You don't send the hash of the token just as you don't send the hash of the password ...
    – schroeder
    May 22, 2020 at 7:10

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .