PHP Conference Japan 2024

openssl_random_pseudo_bytes

(PHP 5 >= 5.3.0, PHP 7, PHP 8)

openssl_random_pseudo_bytes生成伪随机字节字符串

描述

openssl_random_pseudo_bytes(int $length, bool &$strong_result = null): string

生成一个 string 类型的伪随机字节字符串,字节数由 length 参数确定。

它还指示是否使用加密强算法生成伪随机字节,并通过可选的 strong_result 参数执行此操作。这种情况很少出现 false,但某些系统可能已损坏或过旧。

参数

length

所需的字节字符串长度。必须是小于或等于 2147483647 的正整数。PHP 将尝试将此参数转换为非空整数以使用它。

strong_result

如果传递给函数,则它将保存一个 bool 值,该值确定所用算法是否“加密强”,例如,是否适用于 GPG、密码等。如果使用加密强算法,则为 true,否则为 false

返回值

返回生成的 string 类型的字节字符串。

错误/异常

openssl_random_pseudo_bytes() 在失败时抛出 Exception 异常。

变更日志

版本 描述
8.0.0 strong_result 现在可以为空。
7.4.0 该函数不再在失败时返回 false,而是抛出 Exception 异常。

示例

示例 #1 openssl_random_pseudo_bytes() 示例

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

echo
"长度:字节:$i 和十六进制: " . strlen($hex) . PHP_EOL;
var_dump($hex);
var_dump($cstrong);
echo
PHP_EOL;
}
?>

以上示例将输出类似以下内容

Lengths: Bytes: 1 and Hex: 2
string(2) "42"
bool(true)

Lengths: Bytes: 2 and Hex: 4
string(4) "dc6e"
bool(true)

Lengths: Bytes: 3 and Hex: 6
string(6) "288591"
bool(true)

Lengths: Bytes: 4 and Hex: 8
string(8) "ab86d144"
bool(true)

参见

添加注释

用户贡献的注释 8 条注释

35
nahun@telemako
11 年前
这是一个示例,展示了随机数作为图像的分布。感谢 mt_rand 页面上的 Hayley Watson 提供了 rand 和 mt_rand 之间最初的比较。

rand 为红色,mt_rand 为绿色,openssl_random_pseudo_bytes 为蓝色。

注意:这只是数据分布的基本表示。与算法的强度或可靠性无关。



<?php
header
("Content-type: image/png");
$sizex=800;
$sizey=800;

$img = imagecreatetruecolor(3 * $sizex,$sizey);
$r = imagecolorallocate($img,255, 0, 0);
$g = imagecolorallocate($img,0, 255, 0);
$b = imagecolorallocate($img,0, 0, 255);
imagefilledrectangle($img, 0, 0, 3 * $sizex, $sizey, imagecolorallocate($img, 255, 255, 255));

$p = 0;
for(
$i=0; $i < 100000; $i++) {
$np = rand(0,$sizex);
imagesetpixel($img, $p, $np, $r);
$p = $np;
}

$p = 0;
for(
$i=0; $i < 100000; $i++) {
$np = mt_rand(0,$sizex);
imagesetpixel($img, $p + $sizex, $np, $g);
$p = $np;
}

$p = 0;
for(
$i=0; $i < 100000; $i++) {
$np = floor($sizex*(hexdec(bin2hex(openssl_random_pseudo_bytes(4)))/0xffffffff));
imagesetpixel($img, $p + (2*$sizex), $np, $b);
$p = $np;
}

imagepng($img);
imagedestroy($img);
?>
11
powtac 在 gmx.de
8 年前
[编辑注:截至 PHP 5.4.44、5.5.28 和 PHP 5.6.12,此错误已修复]

在 PHP 5.6 之前,openssl_random_pseudo_bytes() **没有**使用“密码学上安全的算法”!
请参阅错误报告 https://bugs.php.net/bug.php?id=70014 和相应的源代码 https://github.com/php/php-src/blob/php-5.6.10/ext/openssl/openssl.c#L5408
11
christophe.weis 在 statec.etat.lu
13 年前
使用 OpenSSL 替换 rand() 的另一种方法。

请注意,使用模运算符 (%) 截断结果的解决方案在密码学上是不安全的,因为生成的数字分布不均匀,即某些数字出现的频率可能高于其他数字。

比使用模运算符更好的解决方案是,如果结果太大则将其丢弃并生成一个新的结果。

<?php
function crypto_rand_secure($min, $max) {
$range = $max - $min;
if (
$range == 0) return $min; // 随机性不高...
$log = log($range, 2);
$bytes = (int) ($log / 8) + 1; // 以字节为单位的长度
$bits = (int) $log + 1; // 以位为单位的长度
$filter = (int) (1 << $bits) - 1; // 将所有低位设置为 1
do {
$rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes, $s)));
$rnd = $rnd & $filter; // 丢弃不相关的位
} while ($rnd >= $range);
return
$min + $rnd;
}
?>
1
mailjeffclayton[at]gmail
5 年前
从给定范围内获取具有均匀分布的整数值

我创建了此函数来解决模结果导致范围结果重叠的问题(这会导致分布不均匀)。

对于不熟悉此问题的人,我的意思是

使用字节表示 256 进制(16 进制)并尝试在某个值的范围内查找值,例如 10-20(跨度为 11),将无法均匀划分,因此值(使用模)将重叠并赋予某些数字比其他数字更高的优先级。

我没有根据字节值进行计算,而是将字节值用作排序的键。这非常快,并且不需要轻易超过 Max Int 值的大量数据空间乘法。

此外:为了使用户提供的参数不关心顺序,我正在结合我下面的函数使用在野外找到的一个方便的交换函数。

// 交换函数

function swap(&$a,&$b) { list($a,$b)=array($b,$a); } // 交换 2 个变量 - 不需要临时变量!

// 用于在给定整数范围内获取随机值的函数

function get_secure_random_ranged_value($max=99, $min=0) // 处理 1 或 2 个参数,顺序无关紧要
{
$sortarray = array();
$lo = (int)$min;
$hi = (int)$max;
if ($lo > $hi) swap($lo,$hi);
$data_range = abs($hi - $lo) + 1; // +1 包括范围的最低“零”值和最高值
$bytes_per_key = 4; // 最大值:ffff 十六进制 = 4,294,967,296 十进制(超过 40 亿) - 大范围的随机值涵盖大量数据集
$num_bytes = $data_range * $bytes_per_key;
$byte_string = (bin2hex(openssl_random_pseudo_bytes($num_bytes))); // 只需调用一次即可获取字节字符串
$byte_blocksize = $bytes_per_key << 1; // 左移乘以 2,因为一个字节宽 2 个字符

while ($key = substr($byte_string,0,$byte_blocksize)) { // 从字符串中获取下一个字节块
$byte_string = substr($byte_string,$byte_blocksize); // 从字符串中移除选定的字节块
$sortarray[]=$key; // 将键暂时作为数组值填充到数组中
}

$sortarray = array_flip($sortarray); // 交换以使用字节值作为键
ksort($sortarray); // 按键随机化
return array_shift($sortarray) + $lo; // 从数组中获取顶部值并将其添加到范围中的最低值
}

//
// 获取 0 到 21 之间值的示例
//

for ($i=1;$i<=10;$i++) { $rnd = get_secure_random_ranged_value(21); echo "-> 结果:".($rnd)." <br />\n"; }

//
// 获取 14 到 21 之间值的示例
//

for ($i=1;$i<=10;$i++) { $rnd = get_secure_random_ranged_value(14,21); echo "-> 结果:".($rnd)." <br />\n"; }

//
// 14-21 的示例结果


//

-> 结果: 14
-> 结果: 18
-> 结果: 20
-> 结果: 15
-> 结果: 20
-> 结果: 16
-> 结果: 21
-> 结果: 15
-> 结果: 16
-> 结果: 17
4
acatalept at gmail
13 年前
FYI,在 Windows 下,openssl_random_pseudo_bytes() 可能会非常慢,以至于无法使用。在我的几台 Windows 机器上,它经常超时(执行时间 > 30 秒)。

显然,这是 OpenSSL 的一个已知问题(不是 PHP 特定的问题)。

参见:http://www.google.com/search?q=openssl_random_pseudo_bytes+slow
5
Tyler Larson
15 年前
如果您没有此函数,但安装了 OpenSSL,则始终可以模拟它

<?php
function openssl_random_pseudo_bytes($length) {
$length_n = (int) $length; // shell 注入不好玩
$handle = popen("/usr/bin/openssl rand $length_n", "r");
$data = stream_get_contents($handle);
pclose($handle);
return
$data;
}
?>
-3
crrodriguez at opensuse dot org
13 年前
请记住,至少请求 8 个字节的熵,理想情况下是 32 或 64 个,以避免可能的理论上的暴力破解攻击。
-4
umairkhi at hotmail dot com
7 年前
在修复了此处不安全的数字生成后

http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-8867

此函数以及此处的文本需要更新。我相信此函数在 FIPS 兼容的应用程序中也可以安全使用,因为它现在使用 RAND_bytes 而不是不安全的 RAND_pseudo_bytes()。
To Top