Artificial truth

The more you see, the less you believe.

[archives] [latest] | [homepage] | [atom/rss]

uniqid in php is not even remotely funny
Sun 23 March 2014 — download

I was reading the source code of festival when I stumbled upon a weirdly implemented function: uniqid(). The documentation states that this is a reimplementation of the php function uniqid. Doesn't smell good.

Chosen excerpts from the php documentation

Description of the function:

Gets a prefixed unique identifier based on the current time in microseconds.

I'm wondering what is happening when I request more than one unique identifier during the same microsecond.

Warning:

This function does not create random nor unpredictable string.

So, why not use a simple stupid counter instead ?

Optional parameter

If set to TRUE, uniqid() will add additional entropy (using the combined linear congruential generator) at the end of the return value, which increases the likelihood that the result will be unique.

Wat. I though that it is supposed to return an unique identifier, not a likely unique one. Appending some pseudo-randomness and hoping that the result will be "more unique" seems great.

Caution

Under Cygwin, the more_entropy must be set to TRUE for this function to work.

Wat. Why the fuck am I supposed to adapt my PHP code to the underlying operating system ?!

Not crypto-compliant

This function does not generate cryptographically secure tokens, in fact without being passed any additional parameters the return value is little different from microtime().

I'm wondering why it's little different and not equal.

Source code

#ifdef HAVE_GETTIMEOFDAY
PHP_FUNCTION(uniqid)
{
    char *prefix = "";
#if defined(__CYGWIN__)
    zend_bool more_entropy = 1;
#else
    zend_bool more_entropy = 0;
#endif
    char *uniqid;
    int sec, usec, prefix_len = 0;
    struct timeval tv;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sb", &prefix, &prefix_len,
                              &more_entropy)) {
        return;
    }

#if HAVE_USLEEP && !defined(PHP_WIN32)
    if (!more_entropy) {
#if defined(__CYGWIN__)
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "You must use 'more entropy' under CYGWIN");
        RETURN_FALSE;
#else
        usleep(1);
#endif
    }
#endif
    gettimeofday((struct timeval *) &tv, (struct timezone *) NULL);
    sec = (int) tv.tv_sec;
    usec = (int) (tv.tv_usec % 0x100000);

    /* The max value usec can have is 0xF423F, so we use only five hex
     * digits for usecs.
     */
    if (more_entropy) {
        spprintf(&uniqid, 0, "%s%08x%05x%.8F", prefix, sec, usec, php_combined_lcg(TSRMLS_C) * 10);
    } else {
        spprintf(&uniqid, 0, "%s%08x%05x", prefix, sec, usec);
    }

    RETURN_STRING(uniqid, 0);
}
#endif

Ok, now I see why more_entropy is required on Cygwin. It's because it doesn't implement usleep(). I don't even know what to say: The whole function and its implementation are completely retarded.

Solution

  • Use the native uuid function of your operating system.
  • Use a stupid counter
  • If you want unique random values, check this code.