Генерация криптографически безопасных случайных чисел в 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 ответов

Решение

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

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

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

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

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

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

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

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

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

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

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

В новом 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 используется там, где ничего из вышеперечисленного недоступно

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

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

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

Каким образом mt_rand() "плохо"?

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

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

<?php
  function random_number(){
      return 4; // return generated number
                // guaranteed to be random
  }
  ?>

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

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

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

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

Изменить: мой комментарий больше не действителен. Пожалуйста, смотрите следующий ответ: /questions/14844192/generatsiya-kriptograficheski-bezopasnyih-sluchajnyih-chisel-v-php/14844233#14844233


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

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

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

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

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

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

Используйте /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();
?>

Я сделал 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/

Tru Случайные числа

<?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;
}
?>

а также криптозащита;)

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

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

Поскольку вся эта случайность зависит от системного времени, давайте возьмемся за системное время! Количество времени, которое операция занимает на компьютере, на самом деле довольно изменчиво (особенно, если на этом сервере происходят другие вещи), поэтому, если мы воспользуемся этим с помощью 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) и измерение времени, которое требуется?

Используя 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;
}

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

Другие вопросы по тегам