views:

143

answers:

1

I would like to create a site-wide hash to be used as salt in creating password retrieval tokens. I have been bouncing around stackoverflow trying to get a sense of the best way to do this.

Here's the reset process:

When a user requests a password reset email the code generates a retrieval token:

$token = hash_hmac('sha256', $reset_hash* , $site_hash)

*$reset_hash is a hash created using phpass HashPassword() function, saved in the user table.

I then send the token in a URL to the users email address. They click before the token times out in an hour. I match their submission with the a challenge token generated server-side. If it matches, then they are forced to choose a new password, and then login.

I would like to know the best way to generate the $site_key. I am thinking of using another HMAC hash that is seeded by random numbers:

$site_key = hash_hmac('sha256', MCRYPT_DEV_RANDOM, MCRYPT_DEV_RANDOM);

This produces something like this:

98bb403abbe62f5552f03494126a732c3be69b41401673b08cbfefa46d9e8999

Will this be a suitably random to be used for this purpose? Am I overcomplicating this, or approaching it the wrong way?

I was inspired to use HMAC by this answer

EDIT: I am trying to avoid a 'secret question' step urged by some of my coworkers, so I would like the reset link to provide a single step to resetting the password. Therefore, my concern is that this process be secure enough to safeguard a system containing sensitive information.

RESOLVED, for now: I am going to go with a nonce as described by The Rook as the reset token. Thanks everyone for the comments and feedback.

+2  A: 

To start with, your not talking about a salt. Your talking about a Cryptographic Nonce, and when you salt a password you should use a Cryptographic Nonce. In the case of resetting passwords, it should be a random number that is stored in the database. It is not advantageous to have have a "site salt".

First and foremost I don't like uniqid() because its a time heavy calculation and time is a very weak seed. rand() vs mt_rand(), spoiler: rand() is total crap. Where as mt_rand() is very solid and a very good choice.

function nonce($size=32){//256 bit == 32byte. 
    for($x=0;$x<$size;$x++){
        $ret.=chr(mt_rand(0,255));
    }
    return base64_encode($ret);
}

Because you are building a url you should do a: $password_token=urlencode(nonce());.

If you where to use this as a salt, which is used in a message digest function then you should do a base64_decode() which will produce a base256 random value. A base256 value has a very high entropy/size ratio, much higher than base16 which is commonly used as the output from a message digest function. Thus md5(uniqid(mt_rand(),true)); is total crap compared to this, however it is commonly used as a nonce in php.

Rook
@TheRook: Thanks for this. Would you advise using $password_token as the full token. Would it be better to use the nonce to salt $reset_hash to create a token that is not stored in the database, but verifiable when the user clicks the reset URL in the email? Would this, again, be overdoing it? I am trying to avoid a 'secret question' step urged by some of my coworkers, so I would like the reset link to provide a single step to resetting the password.
Todd Holmberg
I hate the "Secret question" stuff, generally its one of those things that makes me go "no longer interested" in your site. But I'm always paranoid somebody will hijack my email account and then reset all my passwords .... you can't win. Also, tried supporting OpenID? Its Awesome !
Kent Fredric
Better use the modified Base-64 character set for URLs (see http://tools.ietf.org/html/rfc4648#section-5).
Gumbo
Would a nonce generated using this method be better to use than a UUID?
Todd Holmberg
@Todd Holmberg taking the hash value of a nonce doesn't make it more random, just use this value.
Rook
@Kent Fredric I agree.
Rook
@Todd Holmberg UUID's are massive, i am not exalt sure how they are generated so i cannot say if its more secure. I know it contains a microtime, such that you never have to worry about the same value being generated twice, where as this value is more of a "pure" random number and the same value could be generated multiple times. `uniqid()` like a UUID will always produce a unique value. So it depends on your needs.
Rook
@Gumbo thats awesome i'm gong to use that right now.
Rook
@Gumbo,@TheRook Found this in comments of the [base64_encode()](http://us2.php.net/manual/en/function.base64-encode.php) page: `$output = strtr(base64_encode($input), '+/=', '-_,')`
Todd Holmberg
@Todd Holmberg thats better than a `urlencode()`.
Rook