tags:

views:

84

answers:

5

I'm doing a module which allow users to reset password. I noticed how most websites they provide a confirmation link which contain query string that has a unique hash.

My question is: How can I generate this unique hash each time the same user request forgot password? Should I store this hash in database and use it for verification later on? Will it be safe? Or should I create some sort of algorithm which generate one-time password? How can I generate a OTP?

+4  A: 

Yes, you should

  1. Generate a unique random reset password. See e.g. this answer.
  2. Store it in the database (possibly with an expiry time)
  3. Send e-mail to the user with the hash.
  4. User visits the reset password page with the hash in the query string.
  5. Check the database to see the user associated with the hash and if the expiry time hasn't passed.
  6. If everything checks out, allow the user to reset the password and delete the reset password from the database.

There seems to be a lot a confusion about the generation of the reset password (or whatever you want to call it). Please read the answer I've linked to and don't reinvent the wheel with hashes and weak seeds.

Artefacto
Hi, this is what I need. tks alot!! cheers!!
+1  A: 

1) To generate the unique hash, mix current-time, user-id and some salt (text). For example, if your website is mysite.com, then use this code:

 $hash = md5(time() . $userid . 'mysite');

This will create a nice hash.

2) Yes, store it in database.
3) Yes, as long as you expire the hash after a fixed period of time, it will be secure.
4) No, generating one-time password usually annoys users.

shamittomar
No, it will not because it's predictable.
Artefacto
If he changes the salt considerably, then it will not be predictable. It's not necessary to use 'mysite' exactly.
shamittomar
Hi all, Thanks for the very quick respond. I will go ahead with the database method =)
+1  A: 

Just using some hash function with user's ID, user's salt (you salt the user's password, right?) and pseudo-random data should be OK:

$pwd_reset_hash = hash ( 'sha256' , $user_id . $user_salt, uniqid("",true));

Store it in the DB (together with time of request) as the reset key for this user

 user_id  pwd_reset_hash  time_requested  
===========================================  
 123      ae12a45232...   2010-08-24 18:05

When the user tries to use it, check that the hash matches the user and that the time is recent (e.g. "reset code is valid for 3 hours" or something like that)

delete it from DB when used or expired

Piskvor
I'd advise against this. It's not as bad as `time()`, but it's still not secure. An attacker that measures the latency between him and the server can probably guess the hash at least 1 each 20-50 times.
Artefacto
At least use [`uniqid()`](http://us.php.net/manual/en/function.uniqid.php) in place of microtime...
ircmaxell
Hi both, I understand microtime can be used. But lets say if I were to do a request password module. User provide their valid email and the system sends a email with the confirmation link with query string that contains the userid and the hash. when user receives the email and click on the confirmation link, the system should bring them to the respective page and validate the hash correct? to check this hash again, how can I generate the same hash using microtime??
@Artefacto: Edited. With `uniqid` and a per-user hash, this shouldn't be trivially guessable now, right?
Piskvor
@Piskvor `uniqid` is not particularly good either. At least, if you use it, pass true to the "more entropy" parameter.
Artefacto
Huh? the third param to `hash` expects a bool, so passing `uniqid` to it is pointless. I'd personally do `hash('sha256', uniqid($userid . $usersalt, true));`
ircmaxell
+1  A: 

The way to go is indeed generate some kind of random thing and storing it in the database. As soon as the password is changed you remove the relevant record from the table so that the link cant be used again.

  • Mail the url to the user, including the "token".
  • If the link is visited, check if the token exists, allow user to change password
  • Delete the token from the database.

As said already in the other responses, doing a SHA1 or an MD5 of the userid, combined with microtime and some salt string is usually a safe bet.

Blizz
Hi Blizz, I guess storing in the database is a way to go. Thanks blizz
1) The salt would better be big enough so it can't be guessed by several hashes through brute force 2) Is it that hard to use `mt_rand` each time? There's a function in PHP that creates cryptographically secure random numbers, why the hell would you be reinventing the wheel building a "home-made" random string generator with a cryptographically weak hash like MD5 seeded by weak seeds like `microtime`...
Artefacto
Well nobody says you can't combine all of those. I usually take the userId, 2 random numbers, microtime and some random string.
Blizz
A: 

On relatively easy way is to use an encryption algorithm in counter mode. You keep a secret password, and a counter (the latter typically stored in the database). Each time you need a secret hash, you increment the counter, encrypt it with your password, and convert to text with something like Base64 encoding.

Unlike most of the others that have been mentioned, this one makes it pretty easy to keep track of where things can break down. Basically, there are two possibilities: 1) the encryption algorithm you're using gets broken, or 2) somebody gets to your secret password.

If you use a well-known, widely-used algorithm like AES, the first is a pretty minor concern. If somebody breaks it, they're going to be too busy grabbing a few billion from every bank on the planet to bother with your web site (unless there's some possibility of the web site in question dealing in so much money that they're going to bother with it). For most practical purposes, that leaves only one thing for you need to deal with: ensuring that your secret password stays secret.

That's pretty easy to handle. It's hard to keep things like credit card numbers secret because you need to make them available to the right people under the right circumstances. This is much easier, for the simple reason that there's never any reason to give it out to anybody.

Jerry Coffin
Encryption has nothing to do with this question. You are essentially saying he can use a "secret hash" and a counter and encrypt the combination of the two to generate the one time password. This just means he can use an encryption algorithm as a random number generator seeded by the "secret hash". Now guess what? PHP already has a more efficient and easier to use random number generator, `mt_rand`. You can still generate predictable one time passwords with it by giving the seed with `mt_srand`. Of course, now if someone gets hold of the "secret hash" he can generate all the reset pwds.
Artefacto
@Artefacto: first, you clearly misunderstood what I was saying, because I said nothing about a "secret hash". Second, the fact that you think the Mersenne Twister is in any way comparable shows only that haven't a clue of what you're dealing with or talking about. Though I suspect it was a typo, you were, unfortunately, exactly correct when you used "predictable" instead of "unpredictable".
Jerry Coffin
@Jerry No, I meant "predictable". And replace "secret hash" with "secret password". Maybe you now understand my point?
Artefacto
@Artefacto: No -- in fact, I'm pretty sure you don't understand the question. To be useful, the URL to be **UN** predictable. If it's predictable, anybody else can predict it. The whole point of all this is to prevent that. Being built in and easy to use doesn't justify using something that's thoroughly broken (nor does your lack of understanding justify a down-vote, but I've gotten used to that happening...)
Jerry Coffin
@Jerry. The point is to generate a string that's unpredictable for an attacker. You're suggesting a way to build a one time password that's predictable for you, but not for an attacker. I'm saying you don't need encryption for this and I'm saying making it predictable for you an unnecessary risk.
Artefacto
@Artefacto: but what you're recommending as an alternative makes it predictable to an attacker (and most of the others posted in this thread are the same -- do they honestly believe an attacker can't get a reasonable guess at the server's idea of the current time?) At some point, the URL has to be "predictable" to you or the server won't know to respond to it.
Jerry Coffin
@Jerry I never defended, in this question, the use of `time` as a (sole) source of entropy. Also, the URL doesn't have to be predictable to the server, one can generate in a completely random fashion and store it in a database. However, with a variant of your solution, you wouldn't need storage, you could simply generate an IV and send it along with the user id encrypted with this IV and the secret password.
Artefacto