4

I'm working on developing a Strong Algorithm to reset passwords securely and looking for feedback from the User Community. Here's what I've come up with so far (with help from What are best practices for activation/registration/password-reset links in emails with nonce)

Password Reset process works as follows: When a User requests that a "reset password link be emailed to them"...

  1. Generate a $salt
  2. Prompt the user for the $email address they want to have their "reset password" link sent to.
  3. Retrieve a $key (=secret user-predefined sensitive account data that only they know such as the city they were born in or SSN#Last4)
  4. Create $nonce = hash($email . $key)
  5. Save to table:
    • $nonce (PK)
    • $salt
    • $exp_date
  6. Create $hash =hash($salt . $email . $key)
  7. Email the user a link to reset their password @ URL=...?hash=$hash

When the User clicks on the link we sent them, it brings them to a form:

  • Enter $email
  • Enter $newPassword
  • Confirm $newPassword
  • Prompt for Key Field... ie: "Enter the City you were born in:" Enter $key

When the User submits this form...

  1. Retrieve $hash from the URL
  2. Recreate $nonce = hash($email . $key)
  3. Use $nonce to retrieve $salt from our table (if unexpired).
  4. If hash($salt . $email . $key) == $hash from URL, then the Validation is GOOD!, so we... Update the user's password in the database
  5. Otherwise, we refuse the attempt to change the password

Notes:

  • All $email and $key responses are trimmed & lower-cased before processing to avoid confusion.
  • Regular maintenance task sproc should periodically remove all expired nonces to keep the table clean

What do you think?

Community
  • 1
  • 1
Deborah Cole
  • 2,876
  • 3
  • 19
  • 19
  • 1
    What's more secure about it then just saving a long-enough, random token on the server side and sending it to the user per mail? I don't get it. – Niklas B. Feb 08 '12 at 11:56
  • Niklas: Anyone with the token could hack it. (i.e. anyone who can peek into the user's email account - evil sysadmins, the next guy to use the public PC after the original user forgot to logout, etc). – Deborah Cole Feb 08 '12 at 12:02
  • To get the link, one would have to hack the email account. You can improve security by adding a security question or asking for that "secret user-defined sensitive account data" in the password reset form, but that doesn't require the hashing stuff (or did I miss something?) – Niklas B. Feb 08 '12 at 12:03
  • This method creates a situation where no route into the data exposes all of the variables required to effect the password change. For example, even if someone could peek into the raw exposed tables, they would still lack a crucial piece of data (the email address that requested the password change and its associated hash token) necessary to hack the password change request. – Deborah Cole Feb 08 '12 at 12:11
  • 2
    Your ‘nonce’ is not a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce) as it is neither arbitrary nor only used once. If you request two password resets, you’ll get issued the same nonce. – Gumbo Feb 08 '12 at 12:15
  • 1
    @DeborahCole: If somebody gets access to your database, you have lost. It's as simple as that, really. – Niklas B. Feb 08 '12 at 12:24
  • I would not use social security number, or any part thereof, unless absolutely necessary (see [NIST Guide to Protecting the Confidentiality of Personally Identifiable Information](http://csrc.nist.gov/publications/nistpubs/800-122/sp800-122.pdf)). Instead, if a PIN is necessary, use a security PIN the user can enter during registration. – Go Dan Feb 08 '12 at 13:30
  • Thanks, Dan! Good point. – Deborah Cole Feb 08 '12 at 18:50
  • That's true, Gumbo... I need to either adjust my terminology or generate a true $nonce by adding a random factor into the $nonce hash (& then including that factor in the URL string, right?). I wonder though, if I leave it as it is, do you see an intrinsic problem with overwriting the existing "$nonce" record with a new one (same "$nonce" id) containing a new $salt & $exp_date to match the new $hash being sent to the user? Obviously the old "reset password" email would no longer work, but I'm okay with that. – Deborah Cole Feb 08 '12 at 18:59
  • Yes Niklas: If they obtain write access, game over. I'm thinking more in terms of stolen or hacked snapshot exposure. – Deborah Cole Feb 08 '12 at 19:00
  • @DeborahCole I don’t see the point of this ‘nonce’ anyway. Just generate a sufficiently random value to identify the reset request and ask the user to verify it by answering the secret question. That’s it. If the user’s e-mail account gets compromised, it’s not your fault. That’s why you’re additionally asking for the secret question answer. If they pick a weak question/answer, it’s again not your fault. Just make sure to point out to the user to choose a [good secret question and answer that is *really* secret](http://www.schneier.com/blog/archives/2005/02/the_curse_of_th.html). – Gumbo Feb 09 '12 at 09:09

1 Answers1

0

Couple of issues. By storing the nonce to the DB you completely destroy the meaning of the nonce and therefore weaken the overall security of the application. By storing the salt you are also weakening security because if I gain access to the database I know the "random" salt value you have used for an account, therefore opening you up to rainbow table attacks.

When the User submits this form...

Retrieve $hash from the URL
Recreate $nonce = hash($email . $key)
Use $nonce to retrieve $salt from our table (if unexpired).
If hash($salt . $email . $key) == $hash from URL, then the Validation is GOOD!, so we... Update the user's password in the database
Otherwise, we refuse the attempt to change the password

The above is broken. Since I know you store nonces you have reduced the security of your application, further by storing the salt as well you have weakened security as well. Given the above scenario it is possible for me to retrieve the salt and to deduce the nonce if I have the email + hashing algorithm (you must assume I know your algorithm). Therefore I can "quickly" break the entire database.

Woot4Moo
  • 23,987
  • 16
  • 94
  • 151