Способность угадать следующее значение rand
зависит от способности определять, что srand
вызывалось. В частности, посев srand
с заданным числом приводит к предсказуемому результату ! Из интерактивной подсказки PHP:
[charles@charles-workstation ~]$ php -a
Interactive shell
php > srand(1024);
php > echo rand(1, 100);
97
php > echo rand(1, 100);
97
php > echo rand(1, 100);
39
php > echo rand(1, 100);
77
php > echo rand(1, 100);
93
php > srand(1024);
php > echo rand(1, 100);
97
php > echo rand(1, 100);
97
php > echo rand(1, 100);
39
php > echo rand(1, 100);
77
php > echo rand(1, 100);
93
php >
Это не просто случайность. Большинство версий PHP * на большинстве платформ ** будут генерировать последовательность 97, 97, 39, 77, 93, когда srand
с 1024.
Чтобы было ясно, это не проблема с PHP, это проблема с его реализацией rand
. Та же проблема возникает в других языках, которые используют ту же (или похожую) реализацию, включая Perl.
Хитрость в том, что любая здравомыслящая версия PHP будет предварительно заполнена srand
«неизвестным» значением. О, но это не совсем неизвестно. От ext/standard/php_rand.h
:
#define GENERATE_SEED() (((long) (time(0) * getpid())) ^ ((long) (1000000.0 * php_combined_lcg(TSRMLS_C))))
Итак, это некоторая математика с time()
, PID и результат php_combined_lcg
, который определен в ext/standard/lcg.c
. Я не собираюсь заходить сюда, так как мои глаза застеклены, и я решил прекратить охоту.
Немного Googling показывает, что другие области PHP не обладают лучшими свойствами генерации случайности , и призывает php_combined_lcg
выделиться здесь, особенно этот фрагмент анализа:
Эта функция ( gettimeofday
) не только возвращает нам точную временную метку сервера на серебряном блюде, она также добавляет вывод LCG, если мы запрашиваем «больше энтропии» (из PHP uniqid
).
Да, этоuniqid
. Кажется, что значение php_combined_lcg
- это то, что мы видим, когда смотрим на полученные шестнадцатеричные цифры после вызова uniqid
со вторым аргументом, установленным в истинное значение.
Теперь, где мы были?
О да. srand
,
Итак, если код, из которого вы пытаетесь предсказать случайные значения , не вызывает srand
, вам нужно будет определить значение php_combined_lcg
, которое вы можете получить (косвенно?) Через вызов uniqid
. С этим значением в руке, это возможно , чтобы перебор остальных значений - time()
, то PID и некоторые математики. Связанная проблема безопасности связана с прерыванием сеансов, но здесь будет работать та же техника. Опять же из статьи:
Вот краткое описание шагов атаки, описанных выше:
- дождитесь перезагрузки сервера
- получить уникальное значение
- грубая сила RNG семян из этого
- опросить онлайн статус, чтобы дождаться появления цели
- чередовать опросы состояния с уникальными опросами, чтобы отслеживать текущее время сервера и значение ГСЧ
- идентификатор сеанса перебора против сервера с использованием времени и интервала значений ГСЧ, установленных при опросе
Просто замените этот последний шаг, как требуется.
(Об этой проблеме безопасности сообщалось в более ранней версии PHP (5.3.2), чем у нас в настоящее время (5.3.6), поэтому возможно, что поведение uniqid
и / или php_combined_lcg
изменилось, поэтому этот конкретный метод может больше не работать). YMMV.)
С другой стороны, если код, который вы пытаетесь создать, вызывает srand
вручную , тогда, если они не используют что-то во много раз лучше, чем результат php_combined_lcg
, вам, вероятно, будет гораздо проще угадать значение и заполнить ваш локальный генератор с нужным номером. Большинство людей, которые будут звонить вручную, srand
также не поймут, насколько это ужасно, и поэтому вряд ли будут использовать лучшие значения.
Стоит отметить, что mt_rand
также страдает от этой же проблемы. Посев mt_srand
с известным значением также даст предсказуемые результаты. Опираясь на свою энтропию openssl_random_pseudo_bytes
, вероятно, безопаснее.
tl; dr: для достижения наилучших результатов не заполняйте генератор случайных чисел в PHP, и, ради бога, не выставляйте uniqid
пользователям. Выполнение одного или обоих из них может сделать ваши случайные числа более предсказуемыми.
Обновление для PHP 7:
PHP 7.0 вводит random_bytes
и в random_int
качестве основных функций. Они используют реализацию CSPRNG базовой системы, освобождая их от проблем, с которыми сталкивается генератор случайных чисел. Они практически аналогичны openssl_random_pseudo_bytes
, только без необходимости установки расширения. Polyfill доступен для PHP5 .
*: Исправление безопасности Suhosin изменяет поведение rand
и так mt_rand
, что они всегда появляются заново при каждом вызове. Suhosin предоставляется третьей стороной. Некоторые дистрибутивы Linux включают его в свои официальные пакеты PHP по умолчанию, в то время как другие делают это опцией, а другие полностью ее игнорируют.
**: В зависимости от платформы и используемых библиотечных вызовов будут генерироваться последовательности, отличные от задокументированных здесь, но результаты должны быть повторяемыми, если не используется исправление Suhosin.