生成范围内的安全随机整数 PHP 5.6
Generate a Secure Random Integer in Range with PHP 5.6
我想在 PHP5.6 中生成一个介于 $min
和 $max
之间的安全随机整数。 PHP 中的 rand()
和 mt_rand()
都被认为不是加密安全的。
来自docs:
Caution
This function does not generate cryptographically secure values, and should not be used for cryptographic purposes. If you need a cryptographically secure value, consider using random_int(), random_bytes(), or openssl_random_pseudo_bytes() instead.
PHP 7 添加了 random_int()
(docs),这非常适合我的用例:
random_int — Generates cryptographically secure pseudo-random integers
但是如何在 PHP 5.6 中实现此功能?
我天真的尝试是这样的:
<?php
function secure_rand($min, $max)
{
return (unpack("N", openssl_random_pseudo_bytes(4)) % ($max - $min)) + $min;
}
但我似乎总是在调用 secure_rand(1, 100)
时得到“2”。我还读到,以这种方式使用模数运算会产生偏差。如何在 PHP 5.6 中模拟 random_int()
?
您可以使用 openssl_random_pseudo_bytes,因为它从 PHP 5.3
开始可用
Generates a string of pseudo-random bytes, with the number of bytes determined by the length parameter.
It also indicates if a cryptographically strong algorithm was used to produce the pseudo-random bytes, and does this via the optional crypto_strong parameter. It's rare for this to be FALSE, but some systems may be broken or old.
在 PHP docs
阅读更多内容
找到了一个非常有效的解决方案。在示例中使用了 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 ) );
更新: 源代码中的注释,删除了对 float/int 的强制转换,使用 mb_strlen()
而不是 strlen()
我可以向您介绍 random_compat,PHP 5 个项目中有哪些 polyfill random_bytes()
和 random_int()
? (旁注:在其他项目中,Wordpress 在 4.4 中采用了它。)
function secure_rand($min, $max)
{
return (unpack("N", openssl_random_pseudo_bytes(4)) % ($max - $min)) + $min;
}
即使这是您希望它做的事情,当 ($max - $min)
不是 2 的偶次方时,这也是 biased random number generator。
我想在 PHP5.6 中生成一个介于 $min
和 $max
之间的安全随机整数。 PHP 中的 rand()
和 mt_rand()
都被认为不是加密安全的。
来自docs:
Caution
This function does not generate cryptographically secure values, and should not be used for cryptographic purposes. If you need a cryptographically secure value, consider using random_int(), random_bytes(), or openssl_random_pseudo_bytes() instead.
PHP 7 添加了 random_int()
(docs),这非常适合我的用例:
random_int — Generates cryptographically secure pseudo-random integers
但是如何在 PHP 5.6 中实现此功能?
我天真的尝试是这样的:
<?php
function secure_rand($min, $max)
{
return (unpack("N", openssl_random_pseudo_bytes(4)) % ($max - $min)) + $min;
}
但我似乎总是在调用 secure_rand(1, 100)
时得到“2”。我还读到,以这种方式使用模数运算会产生偏差。如何在 PHP 5.6 中模拟 random_int()
?
您可以使用 openssl_random_pseudo_bytes,因为它从 PHP 5.3
开始可用Generates a string of pseudo-random bytes, with the number of bytes determined by the length parameter.
It also indicates if a cryptographically strong algorithm was used to produce the pseudo-random bytes, and does this via the optional crypto_strong parameter. It's rare for this to be FALSE, but some systems may be broken or old.
在 PHP docs
阅读更多内容找到了一个非常有效的解决方案。在示例中使用了 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 ) );
更新: 源代码中的注释,删除了对 float/int 的强制转换,使用 mb_strlen()
而不是 strlen()
我可以向您介绍 random_compat,PHP 5 个项目中有哪些 polyfill random_bytes()
和 random_int()
? (旁注:在其他项目中,Wordpress 在 4.4 中采用了它。)
function secure_rand($min, $max)
{
return (unpack("N", openssl_random_pseudo_bytes(4)) % ($max - $min)) + $min;
}
即使这是您希望它做的事情,当 ($max - $min)
不是 2 的偶次方时,这也是 biased random number generator。