Генерация криптографически безопасных случайных чисел в php - PullRequest
17 голосов
/ 25 июня 2009

Функция PHP rand() не дает хороших случайных чисел. Поэтому я начал использовать mt_rand(), который, как говорят, дает лучшие результаты. Но насколько хороши эти результаты? Есть ли способы улучшить их снова?

Моя идея:

function rand_best($min, $max) {
    $generated = array();
    for ($i = 0; $i < 100; $i++) {
        $generated[] = mt_rand($min, $max);
    }
    shuffle($generated);
    $position = mt_rand(0, 99);
    return $generated[$position];
}

Это должно дать вам "идеальные" случайные числа, не так ли?

Ответы [ 16 ]

32 голосов
/ 25 июня 2009

Генераторы псевдослучайных чисел (PRNG) очень сложные звери.

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

Фактически выполнение каких-либо дополнительных действий с числом, возвращаемым ГСЧ, на самом деле не увеличивает его случайность, и фактически число может стать менее случайным.

Итак, мой лучший совет: не балуйтесь со значениями, возвращаемыми из PRNG. Используйте PRNG, который достаточно хорош для предполагаемого использования, а если нет, то найдите PRNG, который может дать лучшие результаты, если это необходимо.

И, честно говоря, похоже, что функция mt_rand использует Mersenne twister , который является довольно хорошим PRNG, так что, вероятно, он будет достаточно хорош для наиболее случайное использование.

Однако Mersenne Twister не предназначен для использования в каких-либо контекстах безопасности . См. этот ответ для решения, которое нужно использовать, когда вам нужна случайность для обеспечения безопасности.

Редактировать

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

Следовательно, в операциях, где верхний конец отбрасывается, а нижний возвращается, значение может стать менее случайным, чем исходное значение, возвращаемое из PRNG.

На данный момент я не могу найти хорошего объяснения, но я основал его на документации Java для метода Random.nextInt(int), который предназначен для создания довольно случайного значения в указанном диапазоне. Этот метод учитывает разницу в случайности частей значения, поэтому он может возвращать лучшее случайное число по сравнению с более наивными реализациями, такими как rand() % range.

23 голосов
/ 16 июля 2015

Быстрый ответ:

В новой PHP7 наконец-то появилась поддержка криптографически безопасных псевдослучайных чисел.

int random_int ( int $min , int $max )

Существует также polyfill для PHP5x .

Более длинный ответ


Не существует идеального генератора случайных чисел, и компьютеры используют генератор псевдослучайных чисел для создания последовательностей, которые выглядят случайными. Последовательности выглядят случайными (и проходят некоторые проверки на случайность ), но поскольку существует некоторый алгоритм для его генерации, вы можете повторить алгоритм с абсолютно одинаковыми состояниями и получить тот же результат.

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


Одним из подмножеств генераторов случайных чисел является криптографически безопасных генераторов случайных чисел :

Требования обычного PRNG также удовлетворяются криптографически безопасный PRNG, но обратное неверно. CSPRNG требования делятся на две группы: во-первых, они проходят статистические тесты на случайность; и во-вторых, что они хорошо держатся под серьезным атака, даже когда часть их начального или рабочего состояния становится доступно атакующему

Так что это довольно близко к вашему определению " perfect ". Еще раз без каких-либо условий (кроме изучения криптографии) вы должны попытаться реализовать один из этих алгоритмов и использовать его в своей системе.


Но, к счастью PHP7 это реализовало,

int random_int ( int $min , int $max )

Генерирует случайные целые криптографические числа где непредвзятые результаты имеют решающее значение (т. е. перетасовка колоды покера).

Источники случайности следующие:

  • В Windows используется исключительно CryptGenRandom ()
  • arc4random_buf () используется, если он доступен (обычно для BSD)
  • / dev / arandom используется там, где доступно
  • Системный вызов getrandom(2) (на новых ядрах Linux)
  • / dev / urandom используется, когда ничего из вышеперечисленного недоступно

Это делает все предыдущие ответы устаревшими (и некоторые устаревшими). ​​

17 голосов
/ 25 июня 2009

Я не уверен, что то, что вы сделали, «улучшает» случайность. Насколько я понимаю, вы генерируете 100 случайных чисел, а затем случайным образом выбираете одно из них.

Из того, что я могу вспомнить из моего вероятностного курса, это, вероятно, не увеличивает случайность, так как если в функции генератора есть базовое смещение (mt_rand ()), то оно все равно будет как-то отражено в выводе.

13 голосов
/ 25 июня 2009

Чем mt_rand () "плох"?

Например: если оно предпочитает определенное число. Допустим, mt_rand (1, 10) предпочитает низкие числа в диапазоне, то есть «1» и «2» встречаются в среднем более чем на 10% каждый. Тогда ваше «улучшение» все равно будет страдать от той же проблемы.

Выбор случайного числа из ошибочной последовательности все равно будет ошибочным.

12 голосов
/ 25 июня 2009
<?php
  function random_number(){
      return 4; // return generated number
                // guaranteed to be random
  }
  ?>

Если оставить в стороне все шутки, вы задаетесь философским вопросом о том, что является "случайным" или что является "лучшим". В идеале вы бы хотели, чтобы в течение вашей процедуры в ваших случайных числах было мало шаблонов. Обычно системное время используется в качестве начального числа, но я также использовал предыдущее случайное число в качестве начального числа, предыдущее случайное число в качестве начального числа. Проблема в том, что при наличии достаточно мощного компьютера и полного знания о работающем оборудовании и функции генератора вы сможете предсказать весь набор сгенерированных чисел. Таким образом, если бы у вас был достаточно мощный компьютер (некоторые люди относят Бога к этой категории), который знал все возможные переменные и функции вселенной, вы бы могли прогнозировать каждое событие, которое произошло или произойдет. Большинство генераторов случайных чисел хороши сами по себе, но если вы знаете кого-то, кто может видеть шаблоны, скорее всего, они похожи на парня из Beautiful Mind, и вам следует проверить их в клинике.

По многочисленным просьбам : D

5 голосов
/ 15 апреля 2011

Я написал cronjob, который периодически получает 1000 номеров из random.org (скажем, раз в час) и добавляет их в массив PHP. Всякий раз, когда я хочу, чтобы в моем сценарии были случайные числа, я использую mt_rand (0,1000) для вызова числа из этого. Несколько дополнительных микросекунд накладных расходов, но я получаю действительно случайные числа, основанные на естественном атмосферном шуме.

2 голосов
/ 25 июня 2009

Редактировать: мой комментарий больше не действителен. Пожалуйста, смотрите следующий ответ: https://stackoverflow.com/a/31443898/109561


Полагаю, вы беспокоитесь о распространении mt_rand (). Я проверил это, и это очень уровень, и обе границы включительно.

Я добавил свой тест в комментарии к документации для mt_rand () в руководстве по php, но он был удален глупым модератором из-за слишком длинных политик, чтобы идти сюда.

2 голосов
/ 25 июня 2009

Все зависит от того, для чего вам нужно это случайное число :) Для меня ShuffleBag самый лучший :) 1003 *

1 голос
/ 16 сентября 2012

используйте / dev / ramdom (генератор истинных случайных чисел на устройстве linux) для заполнения mt_rand

<?
$rnd_dev=mcrypt_create_iv(4, MCRYPT_DEV_RANDOM); //need "apt-get install php5-mcrypt"
$seed=ord(substr($rnd_dev, 0, 1))<<24 |
      ord(substr($rnd_dev, 1, 1))<<16 |
      ord(substr($rnd_dev, 2, 1))<<8 |
      ord(substr($rnd_dev, 3, 1));
mt_srand($seed);
echo mt_rand();
?>
1 голос
/ 25 июня 2009

Если вам не нравится встроенная в PHP rand(), вам, вероятно, также не следует использовать их встроенную shuffle(), поскольку она, похоже, основана на их rand().

Я на полпути уверен, что «промышленный стандарт» тасования теперь - это Фишер-Йейтс тасование.

...