46

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?

Anders
  • 64,406
  • 24
  • 178
  • 215
Ian Warburton
  • 1,147
  • 1
  • 10
  • 16
  • 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 '15 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 '15 at 03: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. – SilverlightFox Apr 27 '15 at 10:11
  • @SilverlightFox Store the expiry time where? – Ian Warburton Apr 27 '15 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](http://en.wikipedia.org/wiki/Length_extension_attack). – SilverlightFox Apr 27 '15 at 12:11

3 Answers3

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.

mehov
  • 421
  • 4
  • 9
tim
  • 29,018
  • 7
  • 95
  • 119
  • 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. – martinstoeckli Apr 26 '15 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. – Ian Warburton Apr 27 '15 at 00: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](http://www.martinstoeckli.ch/hash/en/index.php) to get more in-depth information. – martinstoeckli Apr 27 '15 at 06: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. – Ian Warburton Apr 27 '15 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](http://hashcat.net/oclhashcat/#performance). If you take the code as a password, you would have to use a slow salted key-derivation function at least. – martinstoeckli Apr 27 '15 at 11:24
  • @martinstoeckli Why do you think facebook uses such a small token? – Ian Warburton Apr 27 '15 at 12:00
  • @IanWarburton - I do not have a facebook account, so i cannot verify how they solved the password reset function. – martinstoeckli Apr 27 '15 at 19:22
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.

D.W.
  • 98,420
  • 30
  • 267
  • 572
  • 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. – martinstoeckli Apr 27 '15 at 06: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 '15 at 06:50
  • 5
    "… so only a few users have their accounts exposed." this is enough to hash tokens. Isn't it? – Handsome Nerd Mar 02 '16 at 10:17
  • @TTT, I don't think that follows. There might be vulnerabilities that allow reading the contents of the database but don't allow writing to it. – D.W. May 10 '16 at 16:23
  • @martinstoeckli you could just disallow self-serve password reset for admins, and require that admins have their password reset directly through some sort of CLI tool by the sysadmin. – alexw Nov 02 '16 at 05:20
  • 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). – martinstoeckli Nov 02 '16 at 06: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. – Adam Taylor Dec 16 '16 at 03: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.

schroeder
  • 123,438
  • 55
  • 284
  • 319
  • 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 '20 at 07:10