27

Any best practice on how a reset password token should be constructed? I'm thinking:

random 17 characters [a-zA-Z0-9] + a globally unique id + random 17 characters [a-zA-Z0-9].

Is there a better solution, or an industry standard on reset password tokens?

Justin
  • 42,716
  • 77
  • 201
  • 296

1 Answers1

49

There are some important points to consider.

  1. The code should be really random (read from MCRYPT_DEV_URANDOM), and should not be derrived from other user related information.
  2. Ideally the code is base62 encoded (A-Z a-z 0-9) to avoid problems with the Url.
  3. Store only a hash of the token in the database, otherwise an attacker with read access to the database can reset any account.

This leads to the problem that you have to find the hash of the token in the database, after the user clicked the link. There are two possible ways to store the token:

  • You hash the token with a hash algorithm like SHA512 without a salt. This is secure if the token is very strong (minimum length 20 with 0-9 a-z A-Z). Theoretically you have to check whether such a hash already exists before you enter it in the database, in practise this is negligible. I implemented a password-reset class that can handle such tokens.
  • You hash the token with BCrypt and salt. This allows for shorter tokens, but you cannot search for the hashed token in the database. Instead you have to include a row-id in the link to find the token.
Community
  • 1
  • 1
martinstoeckli
  • 23,430
  • 6
  • 56
  • 87
  • Thanks your class (http://www.martinstoeckli.ch/php/StoPasswordReset.zip) is amazing. Specially, `generateRandomBase62String()` and `hashTokenWithBcrypt()`. Bravo. – Justin Nov 17 '13 at 01:24
  • 1
    Regarding #3, I've always done the same thing, for the same reason. On further reflection though, couldn't an attacker with access to the database simply hash a known plain text, store that hash in the database, and then use his chosen plain text in the password reset URL? I suppose the attacker needs access to the database, as well as knowledge about which hashing algorithm was used, but that seems like a much smaller space to search. – Dustin Rasener Jan 30 '14 at 21:17
  • 2
    @DustinRasener - Gaining write access to a database is much more difficult than only read access. Read access you can get with SQL-injection, thrown away backups, with disposed servers... On the other side, if an attacker has privileges to write to the database, he doesn't need a password reset, he can simply overwrite the password hash directly. – martinstoeckli Jan 30 '14 at 21:30
  • 4
    More info on [Should password reset tokens be hashed when stored in a database?](http://security.stackexchange.com/questions/86913/should-password-reset-tokens-be-hashed-when-stored-in-a-database). Please also note that your reset tokens **should expire**. – ᴍᴇʜᴏᴠ May 22 '16 at 11:02
  • 1
    I'd guess the second option would technically defend better against online brute force attacks anyway, as each attempt would have to not simply match against any of the many users, but a single particular user. Though realistically, appropriate lockout logic should render any benefit in that regard negligible to begin with. – Drazen Bjelovuk Aug 14 '17 at 14:45
  • @DrazenBjelovuk - Strong tokens allow for so many possible combinations, that online and offline brute force attacks are out of question. With the second option you need at least 4 additional characters for the id together with a delimiter, with those 5 digits you can also make the tokens of the first option stronger. Let's do some math, a 24 character token can represent 1E43 combinations. If we assume that the hash can be calculated with 100Giga hashes per second, we can still expect 1'000'000'000'000'000'000'000'000 years to find the hash! Of course this only applies to truly random tokens. – martinstoeckli Aug 15 '17 at 12:03
  • Not sure that 3. is accurate. Can't said attacker only reset passwords for accounts that have non-expired password reset tokens? – Daniel Jan 25 '18 at 20:55
  • @Daniel - Good question, but an attacker with read access knows the registered email adresses and can request a password reset at any time. This way (s)he can get fresh tokens and use them. – martinstoeckli Jan 26 '18 at 07:17
  • What is SHA512 approach? how do you find who is the user – Muhammad Umer Feb 27 '18 at 03:43
  • @MuhammadUmer - Just create a new table containing the userid, the hash of the token and the creation date (to calculate the expiration date). There you can search for the hash and if it exist you know this is a legal request and can read the userid. – martinstoeckli Feb 27 '18 at 08:30