Should password reset tokens be hashed when stored in a database?
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
- reset tokens expire and not every user has an active one
- 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.
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.