Cryptographically secure unique id

2019-02-08 14:11发布

问题:

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);

回答1:

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.



回答2:

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.



回答3:

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.