2015-09-23 5 views
1

Я хотел бы создать безопасное случайное целое число между $min и $max в PHP5.6. Как rand(), так и mt_rand() в PHP считаются не криптографически защищенными.Генерировать безопасное случайное целое число в диапазоне с PHP 5.6

От docs:

Внимание

Эта функция не создает криптографически безопасных значений, и не должны использоваться для криптографических целей. Если вам требуется криптографически безопасное значение, попробуйте вместо этого использовать random_int(), random_bytes() или openssl_random_pseudo_bytes().

PHP 7 добавляет random_int() (docs), который соответствует моему потребительной случай прекрасно:

random_int - Генерирует криптографически безопасные псевдослучайные целые

Но как эта функциональность быть достигнуты в PHP 5.6?

Моя наивная попытка была такова:

<?php 
function secure_rand($min, $max) 
{ 
    return (unpack("N", openssl_random_pseudo_bytes(4)) % ($max - $min)) + $min; 
} 

Но я, кажется, всегда получаю "2" при вызове secure_rand(1, 100). Я также прочитал, что использование операции модуля таким образом может привести к смещению. Как я могу эмулировать random_int() в PHP 5.6?

+0

https://github.com/ircmaxell/random_compat стоит глядя на – Crecket

ответ

3

Я мог бы представить вам random_compat, которые polyfills random_bytes() и random_int() в PHP 5 проектов? (Sidenote: среди других проектов он подхвачен Wordpress в 4.4.)

function secure_rand($min, $max) 
{ 
    return (unpack("N", openssl_random_pseudo_bytes(4)) % ($max - $min)) + $min; 
} 

Даже если это делать то, что вы хотели, чтобы это сделать, это biased random number generator когда ($max - $min) не четная степень 2.

+1

Спасибо, Скотт! Я думаю, что это ответ; Я просмотрел random_compat, и это именно то, что я ищу. PHP '' random_int() 'то, что я хочу, и это похоже на следующее лучшее. – Will

0

Вы можете использовать openssl_random_pseudo_bytes, так как он доступен, начиная с PHP 5.3

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

Он также указывает, использовался ли криптографически сильный алгоритм для создания псевдослучайных байтов и делает это с помощью необязательного параметра crypto_strong. Для этого редко бывает ЛОЖЬ, но некоторые системы могут быть разбиты или стары.

Подробнее на PHP docs

+0

Правильно, но это байты, и мне нужно случайное целое число в диапазоне, поддерживая полный диапазон энтропии. – Will

+1

Хорошо, просто посмотрите первый пример и используйте bindec для преобразования в dec, и вы почти закончили. –

2

Нашли довольно хорошо работающее решение. В примере используется расширение mcrypt, но оно также работает с openssl_random_pseudo_bytes(), если вам нужна дополнительная информация, смотрите here.

function secure_rand($min, $max) { 
    $diff = $max - $min; 
    if ($diff < 0 || $diff > 0x7FFFFFFF) { 
     throw new RuntimeException("Bad range"); 
    } 
    $bytes = mcrypt_create_iv(4, MCRYPT_DEV_URANDOM); 

    // if mcrypt is not enabled on your server, you can use this 
    //$bytes = openssl_random_pseudo_bytes(4); 

    // if mbstring is not enabled, you can also use iconv_strlen 
    if ($bytes === false || mb_strlen($bytes, '8bit') != 4) { 
     throw new RuntimeException("Unable to get 4 bytes"); 
    } 

    $ary = unpack("Nint", $bytes); 
    $val = $ary['int'] & 0x7FFFFFFF; // 32-bit safe 
    $fp = $val/2147483647.0; // convert to [0,1] 
    // if you really need a type of int take this 
    // return (int) round($fp * $diff) + $min; 
    // otherwise it will return a float without decimal numbers 
    return round($fp * $diff) + $min; 
} 

var_dump(secure_rand(1, 1000)); 
var_dump(secure_rand(1, 20)); 
var_dump(secure_rand(1, 10)); 
var_dump(secure_rand(1, 5000)); 
var_dump(secure_rand(1, 1111111)); 

Обновлено: комментарии в источнике, удаляются литая плавать/INT, используется mb_strlen() вместо strlen()

+1

Преобразование в float может потерять точность (приводя к ошибкам) ​​или данные утечки (int <-> float-преобразование не является постоянным временем IIRC), а 'strlen()' становится жертвой 'mbstring.func_overload'. –

+0

@ScottArciszewski: обновлено, кстати, не стесняйтесь редактировать мой ответ – swidmann

+0

Спасибо! Я закончил работу с [random_compat] (https://github.com/paragonie/random_compat), но это тоже похоже на хороший вариант. – Will

Смежные вопросы