Reputation: 23062
The normal flow for resetting a user's password by mail is this:
However, maintaining a table and expiring old strings etc seems like a bit of an unnecessary hassle. Are there any obvious flaws in this alternative approach?
Note that the user's password is already stored in a hashed and salted form, and I'm just hashing it once more to get a unique but repeatable string.
And yes, there is one obvious "flaw": the reset link thus generated will not expire until the user changes their password (clicks the link). I don't really see why this would be a problem though -- if the mailbox is compromised, the user is screwed anyway. And there's no risk of reuse, since once the user's password is changed, the reset link will no longer match.
Upvotes: 7
Views: 4037
Reputation: 2751
Just passed by this issue and have an idea:
1) Issue a JWT (Json Web Token) with info about user account. The token has an expiration, say 1 hour.
2) Send the token to the user in an email/link
3) User clicks the link, the token is sent to the server endpoint, the token is validated. If valid, you unpack the token and updates the user account
Any flaws in this approach? No database is touched (except for new user passwords if necessary)
Upvotes: 0
Reputation: 49
let's say on a very rare case, two of your users had the same hashed password even after concatenating random salt to it; there would be a problem right? I guess it wouldn't hurt if you add email or Hashid of user_id to the reset password link.
Upvotes: 0
Reputation: 12560
Actually after thinking about this again, your method is potentially less secure than "The normal flow".
If you just send back HASH(HASH(user's original password))
, I can see scenarios where this can give an attacker leverage:
Scenario 1:
Jim
registers on your site as [email protected]
.Jim
requests a password reset, but doesn't use it. The reset email is left sitting in his inbox for eternity.Jim
changes his email address on your site.[email protected]
is compromised by Bob
.Bob
now runs a bruteforce attack via his distributed GPGPU farm and recovers Jim
's password.Scenario 2:
Jim
uses a the password jimjonesupinthisma!
for his banking account.Jim
registers on your site as [email protected]
. [email protected]
is not in any way associated with Jim
s bank account.[email protected]
is compromised by Bob
.Bob
now requests a reset, he now has HASH(HASH(jim's password))
.Bob
now runs a bruteforce attack via his distributed GPGPU farm and recovers Jim
's password, which he then uses to access Jim
s bank account.Scenario 3:
(Your site uses TLS, users register via TLS.)
Jim
registers on your site as [email protected]
.Bob
requests a password reset on Jim
s account.Bob
works for NSA at Room 641A.Bob
uses his global internet sniffer and obtains HASH(HASH(jim's password))
as it's emailed in plaintext to [email protected]
.Bob
now runs a bruteforce attack via his distributed GPGPU farm and recovers Jim
's password.Variants of scenarios 1 and 2 happen all the time (depending on how strong the hash and password are), I'm not so sure about 3. The point is, your method leeks unnecessary information, which can indeed leverage an attacker against your user.
I suggest you use randomly generated tokens that have nothing to do with the user's password.
Upvotes: 3
Reputation: 53659
If someone accessed your database with password hashes, they would not know the actual passwords. If you implement this system, then they can generate the reset links and reset the passwords themselves. With random strings and knowledge of a compromise, you can invalidate all the random strings, and only users in the process of resetting the password would be compromised even without knowledge of the access. Not a likely scenario, but it might be worth considering given the nominal overhead of random strings.
Upvotes: 3
Reputation: 881635
To remedy the obvious flaw
, add the current date (and more time-related info representing current fraction of a day if even a day is too long) to what you're hashing to generate the mystery string and check it -- this makes the string "expire" (you may check the previous as well as current date or fraction if you want longer "expiry"). So it seems to me that your scheme is quite viable.
Upvotes: 8