How PHP's uniqid works
PHP has a uniqid
function, that creates a unique identifier. This function is sometimes used in a security context, such as for creating tokens for sessions or CSRF protection. This makes it interesting to know how it works exactly and whether we can predict its output.
Uniqid returns the current time
The source can be found in uniqid.c and is basically this:
PHP_FUNCTION(uniqid)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return strpprintf(0, "%08x%05x", tv.tv_sec, tv.tv_usec);
}
It retrieves the current time consisting of seconds and microseconds, and returns them in hexadecimal form. The output of uniqid
consists of the current time.
Sleep a microsecond to avoid doubles
You may have noticed that the current time in microseconds may give the same result twice if we call the function within the same microsecond. To avoid this, uniqid
has a call to usleep
in it:
PHP_FUNCTION(uniqid)
{
struct timeval tv;
usleep(1);
gettimeofday(&tv, NULL);
return strpprintf(0, "%08x%05x", tv.tv_sec, tv.tv_usec);
}
This delays execution for one microsecond, causing the result of uniqid
to be truly unique.
Except when calling it in parallel. If two processes call uniqid
at the same time, they will both sleep for a microsecond and still return the same value.
On Windows, there is no usleep
function, and PHP does not implement any alternative. It simply skips the call to usleep
, causing uniqid
to return the same value every time it is called within the same microsecond. Except on Cygwin, where it raises an error.
Prefix and more entropy parameters
The uniqid
function has two parameters: prefix
and more_entropy
. Prefix is just a string that is prepended to the output.
If the more_entropy
parameter is true, some pseudorandom value is appended to the output. This value is generated by calling php_combined_lcg
, which corresponds to PHP’s lcg_value. Here, “lcg” stands for linear congruential generator, which is a type of pseudorandom number generator. It is not secure and can be easily cracked.
Conclusion
As we have seen the result of uniqid
is not always unique and can be fairly easily predicted, since it is just the current time in hexadecimal.