Статьи

Случайность в PHP — тебе повезло?

Cryptography Randomness in PHP

В этой статье анализируются проблемы, связанные с генерацией случайных чисел, используемых в целях криптографии. PHP 5 не предоставляет простой механизм для генерации криптографически сильных случайных чисел, в то время как PHP 7 решает эту проблему, вводя несколько функций CSPRNG .

Что такое CSPRNG?

Цитируя Википедию , криптографически безопасный генератор псевдослучайных чисел (CSPRNG) — это генератор псевдослучайных чисел (PRNG) со свойствами, которые делают его пригодным для использования в криптографии.

CSPRNG может быть в основном полезен для:

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

Центральным аспектом поддержания высокого уровня безопасности является высокое качество случайности.

CSPRNG в PHP 7

В PHP 7 представлены две новые функции, которые можно использовать для CSPRNG: random_bytes и random_int .

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

Пример:

 $bytes =  random_bytes ( '10' ); var_dump ( bin2hex ( $bytes )); 
 //possible ouput: string(20) "7dfab0af960d359388e6" 

Функция random_int возвращает целое число в заданном диапазоне.
Пример:

 var_dump ( random_int ( 1 ,   100 )); 
 //possible output: 27 

За кулисами

Источники случайности указанных функций различны в зависимости от среды:

  • В Windows всегда будет использоваться CryptGenRandom() .
  • На других платформах будет использоваться arc4random_buf() , если она доступна (верно для производных BSD или систем с libbsd).
  • В противном случае будет использоваться системный вызов getrandom (2) в Linux.
  • Если все остальное терпит неудачу / Dev / Urandom будет использоваться в качестве окончательного отступления.
  • Если ни один из вышеперечисленных источников недоступен, Error .

Простой тест

Хорошая система генерации случайных чисел обеспечивает правильное «качество» поколений. Чтобы проверить это качество, часто выполняется ряд статистических тестов. Не углубляясь в сложные статистические темы, сравнение известного поведения с результатом генератора чисел может помочь в оценке качества.

Один простой тест — игра в кости. Предполагая, что шансы бросить шестерку с одним кубиком равны один к шести, если я брошу три кубика одновременно 100 раз, ожидаемые значения для шестерок 0, 1, 2 и 3 примерно равны:

  • 0 шестерок = 57,9 раз
  • 1 шестерка = 34,7 раза
  • 2 шестерки = 6,9 раза
  • 3 шестерки = 0,5 раза

Вот код для воспроизведения броска костей 1.000.000 раз:

 $times =   1000000 ; $result =   []; 
 for   ( $i = 0 ;  $i < $times ;  $i ++){ $dieRoll =  array ( 6   =>   0 );   //initializes just the six counting to zero $dieRoll [ roll ()]   +=   1 ;   //first die $dieRoll [ roll ()]   +=   1 ;   //second die $dieRoll [ roll ()]   +=   1 ;   //third die $result [ $dieRoll [ 6 ]]   +=   1 ;   //counts the sixes 
 } 
 function  roll (){ 
     return  random_int ( 1 , 6 ); 
 } var_dump ( $result ); 

Тестирование кода выше с PHP7 random_int и простой функцией rand может привести к:

шестерки ожидаемый random_int rand
0 579000 579430 578179
1 347000 346927 347620
2 69000 68985 69586
3 5000 4658 4615

Чтобы увидеть лучшее сравнение между rand и random_int мы можем random_int результаты на графике, применив формулу для увеличения различий между значениями: php result - expected result / sqrt(expected) .

Полученный график будет:

test random graph
(значения, близкие к нулю, лучше)

Даже если комбинация «три шестерки» не работает random_int , а тест слишком прост для реального приложения, мы ясно видим, что random_int работает лучше, чем rand .
Кроме того, безопасность приложения повышается за счет отсутствия предсказуемого, повторяемого поведения в принятом генераторе случайных чисел.

Как насчет PHP 5?

По умолчанию PHP 5 не предоставляет сильных генераторов псевдослучайных чисел. В действительности, есть несколько опций, таких как openssl_random_pseudo_bytes() , mcrypt_create_iv() или прямое использование устройств /dev/random или /dev/urandom с fread() . Есть также пакеты, такие как RandomLib или libsodium .

Если вы хотите начать использовать хороший генератор случайных чисел и в то же время быть готовым к PHP 7, вы можете использовать библиотеку Paragon Initiative Enterprises random_compat . Библиотека random_compat позволяет использовать random_bytes() и random_int() в вашем проекте PHP 5.x.

Библиотека может быть установлена ​​через Composer :

 composer require  paragonie / random_compat 
 require   'vendor/autoload.php' ; $string =  random_bytes ( 32 ); var_dump ( bin2hex ( $string )); 
 // string(64) "8757a27ce421b3b9363b7825104f8bc8cf27c4c3036573e5f0d4a91ad2aaec6f" $int =  random_int ( 0 , 255 ); var_dump ( $int ); 
 // int(81) 

Библиотека random_compat использует другой порядок предпочтений по сравнению с PHP 7:

  1. fread() /dev/urandom если доступно
  2. mcrypt_create_iv($bytes, MCRYPT_CREATE_IV)
  3. COM('CAPICOM.Utilities.1')->GetRandom()
  4. openssl_random_pseudo_bytes()

Для получения дополнительной информации о том, почему этот заказ был использован, я предлагаю прочитать документацию .

Простое использование библиотеки для генерации пароля может быть:

 $passwordChar =   '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' ; $passwordLength =   8 ; $max =  strlen ( $passwordChar )   -   1 ; $password =   '' ; 
 for   ( $i =   0 ;  $i <  $passwordLength ;   ++ $i )   { $password .=  $passwordChar [ random_int ( 0 ,  $max )]; 
 } echo $password ; 
 //possible output: 7rgG8GHu 

Вывод

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

Если вы хотите использовать надежный источник случайных данных, как вы видели в статье, рекомендуется начать как можно скорее с random_int и random_bytes .

Вопросы или комментарии? Оставь их ниже!

Дальнейшее чтение

Описание Ссылка на сайт
Die Hard Test https://en.wikipedia.org/wiki/Diehard_tests
Тест хи-квадрат с примером игры в кости http://bit.ly/1Mrptf5
Тест Колмогорова-Смирнова https://en.wikipedia.org/wiki/Kolmogorov-Smirnov_test
Спектральный тест http://random.mat.sbg.ac.at/tests/theory/spectral/
Набор тестов RaBiGeTe http://cristianopi.altervista.org/RaBiGeTe_MT
Генерация случайных чисел в PHP (2011) http://blog.ircmaxell.com/2011/07/random-number-generation-in-php.html
Тестирование RNG часть 1 и 2 http://ubm.io/1Ot46vL http://ubm.io/1VNzh3N

Подтверждения

Большое спасибо следующим рецензентам за помощь в этой статье!