0

I'm working on a password reset procedure for a website. The correct implementation suggests to store a random salted token hash and expiration date in the database.

But I came across a solution that doesn't store tokens in the database : https://stackoverflow.com/a/3165086/980737

I was thinking about using the above, but replacing sha1() with password_hash() using PASSWORD_BCRYPT.

It looks fine to me but I'm not a security expert. Is the above no database option secure ? If not why ?

Community
  • 1
  • 1
standac
  • 1,027
  • 1
  • 11
  • 26
  • 1
    The answer you linked does a bad job explaining the concepts used. I have a bad feeling because of all these unexplained values that are being used. For example, it kind of defeats the purpose to put the user password into the process when the user wants to reset his password because he forgot it... – Sven Sep 13 '13 at 21:05
  • 1
    sorry , but the link you gave us is a bad example (explanation) – moskito-x Sep 13 '13 at 21:17
  • 1
    @Sven It is the current *hashed* password (which is then hashed again with additional information) that is used as an "assumed" server-secret. The first sentences of the comment are spot-on though: it's a horrible example/link without a good explanation. – user2246674 Sep 13 '13 at 22:04

2 Answers2

3

I would argue the approach linked is "not secure".

The code uses a hand-rolled approach in an attempt to verify the integrity of the URL/token data. The idea is that only the server (which should be the only entity that knows the user password and salt) can generate the given SHA1; on receiving the URL the SHA1 can be regenerated and, if it matches, then the solution proposes that the URL "must" have been server generated and can therefor be trusted.

However, the above assumption might be violated! If the bcrypted/hashed passwords are already known1 then the attacker can generate arbitrary reset URLs and gain access. This is because the attacker has all the required information to forge his own "verifiable" URL.

Applying encryption - after the token generation, to avoid exposing information as Blender pointed out, perhaps - or using an HMAC would increase the knowledge required as an extra "server secret" is used to generate such reset URLs. Encryption and MAC validation is employed in cases where confidentiality and integrity checks of client-stored-and-returned data are performed2.

However, if this additional "server secret" was learned by the attacker, then same issue as mentioned above would still hold and fake reset URLs could be generated at will. (But at this point I suspect that there be other security holes that are more pressing ..)


As far as bcrypt vs. SHA1: there is no point to switch due to the huge input space - the password should already be hashed at this point! - and limited timeout of reset requests. So switching to bcrypt here adds no extra security and using bcrypt does not mitigate the previous issue mentioned. (On the other hand, SHA1 is a poor choice for the actual hashed passwords!)


1 While it would be nice if account/password information is never leaked, the very point of password hashing to begin with is to prevent [mass] exploitation should such a leak occur. Because such leaks are thus taken as a "could happen" scenario (and they do happen), then additional security measures should already assume that such account/password information may not be used as a secure secret.

2 ASP.NET ViewState secuity/validation supports both encryption and integrity verification which shows, that with a solid implementation and security precautions, database/storage-less approaches can work in secure enterprise environments.

user2246674
  • 7,621
  • 25
  • 28
0

Random tokens should be truly random. By making the token a function of non-random data, you leak sensitive information.

If you want to implement this yourself, generate a secure random token:

$token = hex(openssl_random_pseudo_bytes(10, true));

And make it expire within 20 minutes or an hour.

Blender
  • 289,723
  • 53
  • 439
  • 496