I would like to implement a forgotten password scenario in an web application. The system will send out an email to the user containing a unique url that the user can hit to allow them to reset their password. There is loads of guidance on this online. The following is a good linke suggesting how to implement this. Best way of doing code for "Forgotten Password"
The part I do not understand fully is the token generation. What does this mean?? Is this just a guid (or random string) that is stored on the server against the user (maybe in the users db table). The guid is also sent in the url (as querystring) so that when the request hits the web server it can look the guid up and find which user account to reset. Is there more to it than this? Many people talk about token expiration. I could store an expiration time against the guid after which the account reset cannot be done.
Some have suggested a CSRF token, but I cannot understand how this would work in this scenario.
Any guidance would be much appreciated... :)
Storing a randomly generated token of (at least) 128 bits server-side, together with the username and an expiration date, will work perfectly fine.
Another way to achieve the same (without having to store anything server-side) is computing
hash = hash(secret + user name + expiration date)
where +
denotes concenation, hash()
is a cryptographically secure hash function (like SHA2) and secret
is a string of (at least) 128 bits that is only known to you, and send this to the user:
user name + expiration date + hash
Both method achieve the same security, but note that - until the token expires - the user could change his password several times.
In the first case, make sure that token
is created randomly (e.g. using /dev/random
if you're on linux). The same goes for secret
in the second. But secret
is static (not newly generated for every request).
I used this piece of code to generate my token :
/**
* generates a random token, uses base64: 0-9a-zA-Z/+
* @param int [optional] $length length of token, default 24 (144 Bit)
* @return string token
*/
function generateToken($length = 24) {
if(function_exists('openssl_random_pseudo_bytes')) {
$token = base64_encode(openssl_random_pseudo_bytes($length, $strong));
if($strong == TRUE)
return substr($token, 0, $length); //base64 is about 33% longer, so we need to truncate the result
}
//fallback to mt_rand if php < 5.3 or no openssl available
$characters = '0123456789';
$characters .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/+';
$charactersLength = strlen($characters)-1;
$token = '';
//select some random characters
for ($i = 0; $i < $length; $i++) {
$token .= $characters[mt_rand(0, $charactersLength)];
}
return $token;
}
Source : http://www.php.net/manual/en/function.openssl-random-pseudo-bytes.php#96812