Криптографически безопасный уникальный идентификатор

Я хочу создать криптографически безопасные уникальные uuids с помощью php.

uniqid() предоставляет уникальные, но не безопасные идентификаторы, а openssl_random_pseudo_bytes() обеспечивает безопасные, но не уникальные идентификаторы. Является ли сочетание двух (следующий код) правильным подходом или есть лучшее решение?

uniqid(bin2hex(openssl_random_pseudo_bytes(10)), true);

Ответ 1

Я хочу создать криптографически безопасные уникальные uuids с помощью php.

Хорошо, это легко сделать.

uniqid() предоставляет уникальные, но не безопасные идентификаторы, а openssl_random_pseudo_bytes() обеспечивает безопасные, но не уникальные идентификаторы.

Что заставляет вас думать, что криптографически безопасное псевдослучайное число не уникально?

/**
 * 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))
    ]);
}

Приведенный выше пример соответствует спецификации UUIDv4 и использует PHP7 random_bytes().

Для проектов PHP 5 вы можете использовать random_compat до polyfill random_bytes() из PHP 7.

Ответ 2

Почему бы не хешировать вывод openssl_random_pseudo_bytes? Вы также можете конкатрировать временную метку и хеш после нее

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

Использование md5 как пример. Вы можете использовать любой алгоритм хеширования.

Ответ 3

Несмотря на то, что верхний ответ практически правильный, он теоретически не правильный.

У вашего вопроса также нет идеального ответа.

Безопасность зависит от непредвзятой, непредсказуемой, истинной случайности. Но что-то действительно случайное всегда может повторять, или это не было бы случайным. Миллионная матрица могла приземлиться на том же количестве миллион раз подряд, вероятность того, что это происходит, очень мала.

Сила UUIDv4 заключается в том, что вероятность получить один и тот же идентификатор дважды (столкновение) является астрономически малой, "выбирая один и тот же атом из галактики дважды", небольшой.

Любая попытка добавить уникальность фактически уменьшит безопасность. Вы можете добавить микросекундную метку времени или значение автоматического увеличения и миллиметровую пространственную координату, чтобы гарантировать уникальность. Но затем вы добавляете информацию о том, где и как и в каком порядке был создан идентификатор...

Опять же, для всех практических целей, безопасно использовать UUIDv4 как безопасный и уникальный идентификатор.

Также понимайте, что md5, sha1, uniqid и т.д. не идеальны сами по себе, и их объединение случайным образом не обязательно уменьшает вероятность столкновения или повышает безопасность. Хеширующие функции в лучшем случае уникальны, как вещь, которую вы хешируете, и обычно они уменьшают уникальность.

Ответ всегда лежит в случайности плюс длина.