Генерация криптографически безопасных токенов

84

Чтобы сгенерировать 32-символьный токен для доступа к нашему API, мы в настоящее время используем:

$token = md5(uniqid(mt_rand(), true));

Я читал, что этот метод не является криптографически безопасным, поскольку он основан на системных часах, и это openssl_random_pseudo_bytesбыло бы лучшим решением, поскольку его было бы труднее предсказать.

Если это так, как бы выглядел эквивалентный код?

Я предполагаю что-то подобное, но не знаю, правильно ли это ...

$token = md5(openssl_random_pseudo_bytes(32));

И какая длина имеет смысл передать функции?

Огонь
источник
4
Почему мд5 это хоть? Просто преобразуйте поток байтов в шестнадцатеричный: вы получаете 32 байта обратно из openssl_random_pseudo_bytes (), визуализируйте каждый из этих байтов как шестнадцатеричное значение с помощью bin2hex (), как показано в примерах документации PHP
Марк Бейкер,
Мне нужно только 32 символа? Как бы я это сделал?
огонь
10
md5()генерирует строку из 32 символов, но содержит только 128 бит данных. openssl_random_pseudo_bytes()возвращает истинные двоичные данные, поэтому имеет 32 * 8 = 256 битов случайности. Заполняя 32-байтовую случайную строку через md5, вы существенно сокращаете ее уникальность.
Marc B
1
Так $token = bin2hex(openssl_random_pseudo_bytes(16));достаточно, или мне нужен цикл из 16 итераций, передающий 1 в качестве длины и добавляемый в шестнадцатеричном формате к строке?
огонь
Окончательное решение с преобразованием 16 байтов в шестнадцатеричное является правильным. Но вам не следует полагаться на OpenSSL здесь # 1 # 2 # 3 . Как только вы перейдете на PHP 7.0+, больше не будет оправдания его использованию. Вместо OpenSSL и шестнадцатеричного кодирования попробуйте Random::alphanumericString($length), что позволяет уместить эти 16 символов примерно в 2 миллиарда раз больше «энтропии».
каркать

Ответы:

189

Вот правильное решение:

$token = bin2hex(openssl_random_pseudo_bytes(16));

# or in php7
$token = bin2hex(random_bytes(16));
Огонь
источник
1
Я добавил еще один ответ о том, как сгенерировать уникальный токен, используя openssl_random_pseudo_bytes с CryptoLib. Это позволяет вам создавать токены со всеми символами, а не только с 0-9, AF.
mjsa
4
Поддержка PHP 5.x для random_bytes () и random_int () github.com/paragonie/random_compat
MTK,
4
Вы также можете захотеть, bin2hex(random_bytes($length/2))потому что у вас может быть 2 шестнадцатеричных символа для каждого байта, поэтому количество символов всегда будет удваиваться $lengthбез него
Друг
3
@AFriend Согласен. Если вы хотите создать функцию, которая генерирует строку из $lengthчисла символов (а не заботится о размере байта), вы захотите передать random_bytes($length/2).
BadHorsie
10

Если вы хотите использовать openssl_random_pseudo_bytes, лучше всего использовать реализацию CrytoLib, это позволит вам сгенерировать все буквенно-цифровые символы, прикрепление bin2hex вокруг openssl_random_pseudo_bytes просто приведет к тому, что вы получите AF (заглавные буквы) и числа.

Замените path / to / тем местом, где вы поместили файл cryptolib.php (вы можете найти его на GitHub по адресу: https://github.com/IcyApril/CryptoLib )

<?php
  require_once('path/to/cryptolib.php');
  $token = IcyApril\CryptoLib::randomString(16);
?>

Полная документация CryptoLib доступна по адресу: https://cryptolib.ju.je/ . Он охватывает множество других случайных методов, каскадное шифрование, генерацию и проверку хэшей; но он есть, если вам это нужно.

mjsa
источник
Для того же количества байтов: openssl_random_pseudo_bytes(16)каждый байт может иметь любое значение (0-255), тогда как randomString(16)каждый байт может иметь только az Az 0-9, что составляет 62 комбинации на байт. Да, randomString лучше для длины строки , так как bin2hex неэффективное кодирование (50%); лучше использовать base64_encode(openssl_random_pseudo_bytes(16))для более короткой строки и точного известного количества байтов безопасности (в данном случае 16, но при необходимости
Родни
1
@Rodney Имейте в виде основании 64 также будет производить строку , содержащую +, /и =символы, так что если вы пытаетесь сформировать удобный маркер (например , ключ API) он не идеален.
BadHorsie
9

Если у вас есть криптографически безопасный генератор случайных чисел, вам не нужно хэшировать его вывод. На самом деле вы этого не хотите. Просто используйте

$token  = openssl_random_pseudo_bytes($BYTES,true)

Где $ BYTES - сколько байтов данных вы хотите. MD5 имеет 128-битный хеш, поэтому достаточно 16 байт.

В качестве примечания: ни одна из функций, которые вы вызываете в исходном коде, не является криптографически безопасной, большинство из них достаточно опасны, поэтому использование только одной из них будет небезопасным, даже в сочетании с другими безопасными функциями. У MD5 есть проблемы с безопасностью (хотя для этого приложения они могут не иметь отношения). Uniqid не просто не генерирует криптографически случайные байты по умолчанию (поскольку он использует системные часы), добавленная энтропия, которую вы передаете, комбинируется с использованием линейного конгруэнтного генератора, что не является криптографически безопасным. Фактически, это, вероятно, означает, что можно было угадать все ваши ключи API, получив доступ к некоторым из них, даже если они не знали значение часов вашего сервера. Наконец, mt_rand (), то, что вы используете в качестве дополнительной энтропии, также не является безопасным генератором случайных чисел.

Imichaelmiers
источник
27
Второй аргумент openssl_random_pseudo_bytes () должен быть переменной, передаваемой по ссылке. После openssl_random_pseudo_bytes эта переменная будет иметь значение true или false, если openssl смог использовать криптостойкий алгоритм. Он не используется для указания openssl использовать криптостойкий алгоритм, который он все равно попытается сделать.
Jonathan Amend
-1

Надежные пароли Вы можете составлять только из символов ascii a-zA-Z и цифр 0-9 . Для этого лучше всего использовать только криптографически безопасные методы, такие как random_int () или random_bytes () из PHP7. Остальные функции как base64_encode () Вы можете использовать только как вспомогательные функции, чтобы сделать строку надежной и изменить ее на символы ASCII .

mt_rand () небезопасен и очень старый. Из любой строки Вы должны использовать random_int (). Из двоичной строки Вы должны использовать base64_encode (), чтобы сделать двоичную строку надежной, или bin2hex, но тогда Вы сократите байт только до 16 позиций (значений). См. Мою реализацию этой функции. Он использует все языки.

Даро из Польши
источник