10

Which is better?

  1. Create tamper-proof encrypted password reset token which contains the user id and the expiration time within the encrypted token.

  2. Generate a random token and store it in the database with the user id and expiration date. Look up the token when the user clicks the link.

An advantage I can see with #2 is that you keep control over these tokens. If you want to expire it sooner, you can. A difficulty I see with #1 is making sure the tokens you send really are secure and tamper-proof. You're relying entirely on not screwing up the cryptography.

Does ASP.NET 2.0 have any built-in method for generating password reset tokens?

Jeff Ferland
  • 38,170
  • 9
  • 94
  • 172
John
  • 2,262
  • 2
  • 28
  • 45

5 Answers5

14

Creating a random token containing absolutely no information and linking it in the database with a username and an expiration time is by far the best solution.

Encryption (and hashing) is used to store and transfer data that absolutely must be sent. If there is any way to do something without having to send that data, encrypted or not, that provides equal functionality and security, you should use that method. In other words, encryption should be a last resort for securing data.

Eric
  • 451
  • 2
  • 4
4

These tokens are password equivalents, and as such must meet similar requirements. But by far, the biggest is that it must not be arbitrarily guessable by an adversary. It should therefore be indistinguishable from random data.

When you encode any data, obviously it needs to be encrypted. Now, this will likely involve some level of rolling your own, or at least putting the crypto primitives together yourself, which you'll likely do wrong (See: Don't be a Dave).

So, this means that your safest and simplest solution is to create a very random token, and store it hashed/salted in the database, as well as an id for that token. Join the id and token, and serialize it to hex, or Base32 and send it off to the user. When the token comes back in, use the id to grab the token/salt from the database, and hash the rest of the token to see if it matches.

You must ensure several extra things about tokens since they'll be in people's emails.

  • Ensure they have a high enough entropy to resist guessing
  • They must be short enough to encode into a URL comfortably
  • They must expire after a reasonably short window of time as they will be in people's email inboxes
  • They must one-use only
Tinned_Tuna
  • 1,018
  • 7
  • 12
  • 2
    Why does the token need to be salted? I thought the purpose for salting was, in the event of a large password breach, to prevent on cracked password from immediately cracking the same password in the list if multiple people chose the same password. In this case, the token would be cryptographically random, unique... (I do understand hashing it to provide some protection, in case of a SQL injection vulnerability). – John Feb 25 '13 at 19:54
  • The basic idea is to defend the token (which is a password-equivalent) with the same level of protections that come with protecting a password. You should also be very wary of timing attacks against the one-time tokens. – Tinned_Tuna Feb 25 '13 at 22:56
2

You could encode the expire time into a token and use an HMAC, but generally a password reset involves visiting a specific URL. Usually to make things easier for the user, one includes the token in the URL as a GET field.

Wanting to keep the URL relatively small and only made of printable characters, you'd probably encode the timestamp as an integer and use fixed-width settings. This has to specify the user, the expiration, and the MAC. I'd call it reliable, but it's not without some sort of coding effort.

Since you need to hit a database for passwords anyway and it doesn't take any thinking to encode a random fixed-width value, though, I'd just go with that. All else being equal (and I think it is rather equal), easy wins out. Added bonus: lower CPU usage.

Jeff Ferland
  • 38,170
  • 9
  • 94
  • 172
2

It is a common theme. Your scenario #1 is a storage optimization over scenario #2. In #2, you have to generate and store a random value server side. This gives you all the control you may want, but requires an extra column in your database, or something like that. Scenario offloads this storage requirement client-side. Since the client cannot be trusted, you have to add to the link an integrity check, i.e. a MAC; encryption is not needed because there is nothing in the reset link that you need to keep secret from the user (the user already knows his name and when he asked for a password reset), but you want to prevent a malicious client from creating a fake reset link, hence the MAC.

The token contents, in scenario #1, must therefore include all the data that you need to enforce your password reset policy, but chose not to store on the server; this means the user name and a time stamp (time at which the password reset process was triggered, or expiry time). You might want to include a hash of the previous password in the input to the MAC (not necessarily in the token) if you want to prevent the user from reusing the reset link several times.

Computing and verifying a MAC won't take a lot of resources, probably a tad less than generating a random token and storing it in a database (the database access will be considerably more expensive than the cryptography). However, chances are that this effect is negligible anyway (databases are fast). A disadvantage of scenario #1 is that you need to keep a secret key on the server, shared between the front-ends (if you have several), resilient to reboots (so not a key in RAM only, unless you are ready to invalidate all password reset links when the server reboots), and, of course, safe from eavesdroppers.

The random token (scenario #2) is probably easier, or, if you prefer, harder to get wrong. Which is a good enough reason to recommend it.

Tom Leek
  • 170,038
  • 29
  • 342
  • 480
  • 1
    I'm not at all concerned about performance. Any differences are negligible. But "harder to get wrong" is great :-) – John Feb 25 '13 at 19:41
2

I recommend this article from Troy Hunt : Everything you ever wanted to know about building a secure password reset feature

Here is what he is saying on how to handle reset token :

What we want to do is create a unique token which can be sent in an email as part of the reset URL then matched back to a record on the server alongside the user’s account thus confirming the email account owner is indeed the one attempting to reset the password. For example, the token may be “3ce7854015cd38c862cb9e14a1ae552b” and is stored in a table alongside the ID of the user performing the reset and the time at which the token was generated (more on that in a moment). When the email is sent out, it contains a URL such as “Reset/?id=3ce7854015cd38c862cb9e14a1ae552b” and when the user loads this, the page checks for the existence of the token and consequently confirms the identity of the user and allows the password to be changed.

There is also a cheat sheet on the OWASP wiki : Forgot Password Cheat Sheet but they do not say if it is better or not to store the information in the database.

null
  • 1,193
  • 6
  • 16
  • 1
    Already read his article. That's what prompted the question. He doesn't specify which strategy is preferred. – John Feb 25 '13 at 19:27