I want to generate identifier for forgot password . I read i can do it by using timestamp with mt_rand(), but some people are saying that time stamp might not be unique every time. So i am bit of confused here. Can i do it with using time stamp with this ?
Question
What's best practice to generate random/unique tokens of custom length?
I know there are lot of questions asked around here but i am getting more confused after reading different opinion from the different people.
The earlier version of the accepted answer (
md5(uniqid(mt_rand(), true))
) is insecure and only offers about 2^60 possible outputs -- well within the range of a brute force search in about a week's time for a low-budget attacker:mt_rand()
is predictable (and only adds up to 31 bits of entropy)uniqid()
only adds up to 29 bits of entropymd5()
doesn't add entropy, it just mixes it deterministicallySince a 56-bit DES key can be brute-forced in about 24 hours, and an average case would have about 59 bits of entropy, we can calculate 2^59 / 2^56 = about 8 days. Depending on how this token verification is implemented, it might be possible to practically leak timing information and infer the first N bytes of a valid reset token.
Since the question is about "best practices" and opens with...
...we can infer that this token has implicit security requirements. And when you add security requirements to a random number generator, the best practice is to always use a cryptographically secure pseudorandom number generator (abbreviated CSPRNG).
Using a CSPRNG
In PHP 7, you can use
bin2hex(random_bytes($n))
(where$n
is an integer larger than 15).In PHP 5, you can use
random_compat
to expose the same API.Alternatively,
bin2hex(mcrypt_create_iv($n, MCRYPT_DEV_URANDOM))
if you haveext/mcrypt
installed. Another good one-liner isbin2hex(openssl_random_pseudo_bytes($n))
.Separating the Lookup from the Validator
Pulling from my previous work on secure "remember me" cookies in PHP, the only effective way to mitigate the aforementioned timing leak (typically introduced by the database query) is to separate the lookup from the validation.
If your table looks like this (MySQL)...
... you need to add one more column,
selector
, like so:Use a CSPRNG When a password reset token is issued, send both values to the user, store the selector and a SHA-256 hash of the random token in the database. Use the selector to grab the hash and User ID, calculate the SHA-256 hash of the token the user provides with the one stored in the database using
hash_equals()
.Example Code
Generating a reset token in PHP 7 (or 5.6 with random_compat) with PDO:
Verifying the user-provided reset token:
These code snippets are not complete solutions (I eschewed the input validation and framework integrations), but they should serve as an example of what to do.
In PHP, use
random_bytes()
. Reason: your are seeking the way to get a password reminder token, and, if it is a one-time login credentials, then you actually have a data to protect (which is - whole user account)So, the code will be as follows:
Update: previous versions of this answer was referring to
uniqid()
and that is incorrect if there is a matter of security and not only uniqueness.uniqid()
is essentially justmicrotime()
with some encoding. There are simple ways to get accurate predictions of themicrotime()
on your server. An attacker can issue a password reset request and then try through a couple of likely tokens. This is also possible if more_entropy is used, as the additional entropy is similarly weak. Thanks to @NikiC and @ScottArciszewski for pointing this out.For more details see
This answers the 'best random' request:
Adi's answer1 from Security.StackExchange has a solution for this:
1. Adi, Mon Nov 12 2018, Celeritas, "Generating an unguessable token for confirmation e-mails", Sep 20 '13 at 7:06, https://security.stackexchange.com/a/40314/
You can also use DEV_RANDOM, where 128 = 1/2 the generated token length. Code below generates 256 token.
This may be helpful whenever you need a very very random token