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


Функция 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];
}

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

Author: Salvador Dali, 2009-06-25

16 answers

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

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

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

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

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

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

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

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

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

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

 32
Author: coobird, 2017-12-11 19:12:29

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

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

int random_int ( int $min , int $max )

Также существует полифилл для PHP5x.

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


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

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


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

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

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


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

int random_int ( int $min , int $max )

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

Источниками случайных являются следует:

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

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

 22
Author: Salvador Dali, 2017-12-11 19:14:37

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

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

 17
Author: Peter, 2009-06-24 23:44:33

В каком смысле mt_rand() "плохой"?

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

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

 13
Author: truppo, 2009-06-24 23:44:52
<?php
  function random_number(){
      return 4; // return generated number
                // guaranteed to be random
  }
  ?>

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

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

 12
Author: mandroid, 2009-06-24 23:49:06

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

 5
Author: elliot, 2011-04-14 22:22:27

Все зависит от того, для чего вам нужно это случайное число:) Для меня Сумка для перетасовки является лучшей:)

 2
Author: Thinker, 2009-06-24 23:46:46

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


Я предполагаю, что вы беспокоитесь о распределении mt_rand(). Я протестировал его, и он очень ровный, и обе границы являются всеобъемлющими.

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

 2
Author: Gerry, 2017-05-23 12:17:45

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

Я наполовину уверен, что "стандартная для отрасли" перетасовка сейчас - это перетасовка Фишера-Йейтса.

 1
Author: Mark Rushakoff, 2009-06-24 23:46:14

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

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

 1
Author: Sev, 2009-06-25 18:03:00

Используйте /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
Author: diyism, 2012-09-16 09:39:16

Я создал класс PHP для генерации случайных чисел и строк phprandomvalue

Он использует "mcrypt_create_iv(4, MCRYPT_DEV_URANDOM)" для генерации случайных чисел и значений. Я сделал это во время работы над криптопроектом, потому что мне нужен был безопасный генератор случайных величин. Вот пример использования

$randomValue = new RandomValue;

$randomValue->randomNumber(): = -3880998

$randomValue->randomNumberBetween(1,10): = 2

$randomValue->randomTextString(): = CfCkKDHRgUULdGWcSqP4

$randomValue->randomTextString(10):  = LorPIxaeEY

$randomValue->randomKey(): = C7al8tX9.gqYLf2ImVt/!$NOY79T5sNCT/6Q.$!.6Gf/Q5zpa3

$randomValue->randomKey(10):  = RDV.dc6Ai/
 0
Author: ejfrancis, 2014-05-26 23:58:07

Невозможно генерировать истинные случайные числа, лучшее, на что вы можете надеяться, - это псевдослучайность, которую обеспечивает rand(), ваша функция не ближе к случайной, чем rand(). Взгляните на это http://en.wikipedia.org/wiki/Random_number_generator

 -1
Author: UnkwnTech, 2009-06-24 23:45:37

Истинные случайные числа

<?php
for ($i = -1; $i <= 4; $i++) {
    $bytes = openssl_random_pseudo_bytes($i, $cstrong);
    $hex   = bin2hex($bytes);

    echo "Lengths: Bytes: $i and Hex: " . strlen($hex) . PHP_EOL;
    var_dump($hex);
    var_dump($cstrong);
    echo PHP_EOL;
}
?>

А также криптозащищенный;)

 -1
Author: ucefkh, 2013-10-13 06:13:02

Хотя ответ был принят много лет назад, я снова открою его.

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

$a='';
for (int $i=0; $i<9001; $i++)
{
    usleep(mt_rand(1000,10000));//Also eliminates timing attacks... possibly?
    $a=hash('SHA512',$a.uniqid(mt_rand().microtime(),true));
}
echo $a;

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

Знаете ли вы о каких-либо операциях на PHP, которые занимают действительно случайное количество времени? Нравиться... HTTP-запрос на какой-либо веб-сайт (кроме RANDOM.org ) и измерение времени, которое на это требуется?

 -2
Author: oink, 2014-01-03 01:54:06

Использование random.org, вы можете использовать это:

function getToken($length, $min, $max){
    $r = explode('
',file_get_contents('http://www.random.org/integers/num='.$length.'&min='.$min.'&max='.$max.'&col=1&base=10&format=plain'));

    $string = '';
    foreach ( $r as $char ) $string.=$char;
    return $string;
}

Это должно дать реальные случайные числа

 -4
Author: Ben, 2013-04-25 21:19:02