13

I want to generate cryptographically secure unique uuids using php.

uniqid() provides unique but not secure ids and openssl_random_pseudo_bytes() provides secure but not unique ids. Is the combination of the two(following code) a proper approach or is there a better solution?

uniqid(bin2hex(openssl_random_pseudo_bytes(10)), true);
Stavros
  • 655
  • 7
  • 16

3 Answers3

11

I want to generate cryptographically secure unique uuids using php.

Okay, that's easily done.

uniqid() provides unique but not secure ids and openssl_random_pseudo_bytes() provides secure but not unique ids.

What makes you think a cryptographically secure pseudorandom number isn't unique?

/**
 * Return a UUID (version 4) using random bytes
 * Note that version 4 follows the format:
 *     xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
 * where y is one of: [8, 9, A, B]
 * 
 * We use (random_bytes(1) & 0x0F) | 0x40 to force
 * the first character of hex value to always be 4
 * in the appropriate position.
 * 
 * For 4: http://3v4l.org/q2JN9
 * For Y: http://3v4l.org/EsGSU
 * For the whole shebang: https://3v4l.org/LNgJb
 * 
 * @ref https://stackoverflow.com/a/31460273/2224584
 * @ref https://paragonie.com/b/JvICXzh_jhLyt4y3
 * 
 * @return string
 */
function uuidv4()
{
    return implode('-', [
        bin2hex(random_bytes(4)),
        bin2hex(random_bytes(2)),
        bin2hex(chr((ord(random_bytes(1)) & 0x0F) | 0x40)) . bin2hex(random_bytes(1)),
        bin2hex(chr((ord(random_bytes(1)) & 0x3F) | 0x80)) . bin2hex(random_bytes(1)),
        bin2hex(random_bytes(6))
    ]);
}

The above example conforms to the UUIDv4 specification and uses PHP7's random_bytes() function.

For PHP 5 projects, you can use random_compat to polyfill random_bytes() from PHP 7.

Scott Arciszewski
  • 33,610
  • 16
  • 89
  • 206
  • For those using php, see https://stackoverflow.com/a/18890309/3774582 for an example of openssl_random_pseudo_bytes() to create a cryptographically secure unique id. – Goose May 29 '17 at 14:32
  • 1
    @Goose Use random_compat, **not** openssl_random_pseudo_bytes: https://github.com/ramsey/uuid/issues/80 – Scott Arciszewski May 30 '17 at 06:40
0

Even though the top answer is practically correct, it's not theoretically correct.

Your question doesn't have a perfect answer either.

Security relies on unbiased, unpredictable, true randomness. But something which is truly random can always repeat, or it wouldn't be random. A million-sided die could land on the same number a million times in a row, the probability of that happening is just very small.

The strength of UUIDv4 is that the probability of getting the same ID twice (collision) is astronomically small, the "pick the same atom from a galaxy twice" kind of small.

Any attempt to add uniqueness will in fact decrease security. You could add a microsecond timestamp or auto-incrementing value and a millimeter precision spatial coordinate to guarantee uniqueness. But then you add information about where and how and in what order the ID was created...

Again, for all practical purposes, it's safe to use UUIDv4 as a safe and unique identifier.

Also realize that md5, sha1, uniqid, etc are not perfect by themselves, and combining them in random ways does not necessarily decrease collision or increase security. Hashing functions are at best as unique as the thing you are hashing, and usually they decrease uniqueness.

The answer always lies in randomness plus length.

okdewit
  • 2,406
  • 1
  • 27
  • 32
  • We're actually talking about greater-than a several billion-sided die and the probability of your rolling the same thing x times is precisely `n^x`. It's so astronomical it took 6,610 years of processing power to find a sha1 collision. Also, combining them certainly does reduce collisions by the product of the probabilities. – Bluebaron Aug 28 '17 at 20:26
-1

Why not hashing the output of openssl_random_pseudo_bytes? You could also concat a timestamp and hash it afterwards

md5(bin2hex(openssl_random_pseudo_bytes(10)).strval(time()));

Using md5 just as an example. You can use any hash algorithm.

devnull
  • 558
  • 4
  • 18