2
$\begingroup$

I've been searching around the web for best practice how-to on secure password reset system (email with reset url), and the idea to hash the token in the database, which I did not implement at first, seems to be the the most secure way to store the token.

Im currently following this guide, and he does not mention it (why?)

Im using bcrypts for password hashing allready, so thats what im using for this aswell.

How ever, the syntac for the verify is like this: Bcrypt.verify(non-hash, hash);

So my question is this: If the reset-link only contains the token (which is not in db, the hash is) - how can I find the user in the db?

is it safe to append email/userID to the reset-link as well? None of the other services Ive seen have does this (afaik), only the token (do they not hash the token in the db?).

I dont want to make my own scheme that will create a security issue, so how do I proceed with this ?

Using asp.net web forms and c#

$\endgroup$
2
  • $\begingroup$ Asuming the token are unique, why not hashing them when you receive them in the URL and looking up the hash in the database? $\endgroup$
    – Alpha
    May 20, 2015 at 0:53
  • $\begingroup$ that was my plan aswell, but the thing is to verify hash I need to use Bcrypts verify method which takes nonhass + hash to verify the hash. I only have the hash from the url. so to make this an option i have to iterate all users and Bcrypt.Verify on all of them to see which is correct (which is not a good idea). $\endgroup$
    – Contra
    May 22, 2015 at 13:51

2 Answers 2

2
$\begingroup$

I was pondering this as well and it was hard to find answers.

My solution: don't hash the token with bcrypt. Instead hash it with e.g. SHA256 with no salt. It will be secure enough if the token is truly random data and long enough.

Then the hash will always be the same, so you can easily find it in the database. No need to iterate over all users or tokens in the database – that would be madness.

Some resources to validate my solution:

$\endgroup$
4
  • $\begingroup$ Replacing a KDF with a regular hash like SHA256 and skipping the salt is something you do NOT want to do when implementing password hashing, verification, and/or storage! (Do rainbow tables ring a bell?) At the very least, use a HMAC for storage. $\endgroup$
    – e-sushi
    May 14, 2018 at 8:05
  • $\begingroup$ @e-sushi I agree that hashing passwords with SHA256 and no salt should not be done. But the question and my answer are about password reset tokens, not about passwords. How would you recommend hashing and verifying tokens? $\endgroup$
    – mts knn
    May 14, 2018 at 13:31
  • $\begingroup$ Oops, missed the fact it was limited to reset tokens. In that case, your suggestion should indeed suffice. Sorry for the noise. I hope my upvote can make it up to you. ;) $\endgroup$
    – e-sushi
    May 14, 2018 at 16:56
  • $\begingroup$ @e-sushi No problem. Thanks for verifying that my suggestion should be okay. :^) $\endgroup$
    – mts knn
    May 14, 2018 at 18:00
0
$\begingroup$

You need one property for your token and one for your hash. The reset token must be unpredictable. The hash of the token must be preimage resistant.

To make an unpredictable token you can take 16 bytes from something like /dev/urandom. Or any RNG with equivalent security properties. (Java's SecureRandom, Python's os.urandom, .NET's RNGCryptoServiceProvider, etc.) If an attacker can guess the next token she can change a person's password without needing to read the account owner's emails.

Preimage resistance is needed for the token's hash. Otherwise someone with read access to the reset token list (admins or hackers) can compute their own token that matches the hash. You should replace BCrypt with SHA-2 when you aren't hashing things like passwords.

The purpose of BCrypt is to stretch low entropy passwords. (Meaning relatively predictable.) If passwords always had high entropy then you could use any preimage resistant hash. The goal of BCrypt is to slow down everyone's guess-and-check speed for passwords and hashes... but normal cryptographic hash functions could be orders of magnitude faster than SHA-2 and still be un-brute-force-able if the input entropy is high. (This is why you generate a long unpredictable token. 16 bytes from urandom should give unpredictability equivalent to 128 bits of entropy.)

DO NOT base which account's email you reset off of client provided data! Put the token, expiration time, and userid all in the same database entry.

Do use a library that implements password management for you. You can use what you learn about cryptography to judge if a library is secure or not, if you learn more, but it will take a much deeper understanding of topics in security and cryptography before you can implement your own system correctly.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.