PHP 8.1.31 Released!

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 现在可为 null。
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
"Lengths: Bytes: $i and Hex: " . 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)

参见

  • random_bytes() - Get cryptographically secure random bytes
  • bin2hex() - 将二进制数据转换为十六进制表示
  • crypt() - 单向字符串散列
  • random_int() - 获取生成加密安全、均匀分布的整数
添加备注

用户贡献的备注 8 notes

up
35
nahun@telemako
11 years ago
Here's an example to show the distribution of random numbers as an image. Credit to Hayley Watson at the mt_rand page for the original comparison between rand and mt_rand.

rand is red, mt_rand is green and openssl_random_pseudo_bytes is blue.

NOTE: This is only a basic representation of the distribution of the data. Has nothing to do with the strength of the algorithms or their reliability.

<?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);
?>
up
11
powtac at gmx dot de
8 years ago
[Editor's note: the bug has been fixed as of PHP 5.4.44, 5.5.28 and PHP 5.6.12]

Until PHP 5.6 openssl_random_pseudo_bytes() did NOT use a "cryptographically strong algorithm"!
See bug report https://bugs.php.net/bug.php?id=70014 and the corresponding source code at https://github.com/php/php-src/blob/php-5.6.10/ext/openssl/openssl.c#L5408
up
11
christophe dot weis at statec dot etat dot lu
13 years ago
Another replacement for rand() using OpenSSL.

Note that a solution where the result is truncated using the modulo operator ( % ) is not cryptographically secure, as the generated numbers are not equally distributed, i.e. some numbers may occur more often than others.

A better solution than using the modulo operator is to drop the result if it is too large and generate a new one.

<?php
function crypto_rand_secure($min, $max) {
$range = $max - $min;
if (
$range == 0) return $min; // not so random...
$log = log($range, 2);
$bytes = (int) ($log / 8) + 1; // length in bytes
$bits = (int) $log + 1; // length in bits
$filter = (int) (1 << $bits) - 1; // set all lower bits to 1
do {
$rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes, $s)));
$rnd = $rnd & $filter; // discard irrelevant bits
} while ($rnd >= $range);
return
$min + $rnd;
}
?>
up
1
mailjeffclayton [at] gmail
5 years ago
Getting an integer value from a given range with an even distribution:

This function I created to solve the problem of modulo results causing overlap of ranged results (which gave an uneven distribution).

What I mean for those not as familiar with the problem:

Using bytes for base 256 (base 16) and attempting to find a value in a range of values that may be for example 10-20 (a spread of 11) will not divide evenly, so values (using mod) will overlap and give more priority to some numbers than others.

Instead of calculating based on byte values, I used the byte values as keys to sort. This is very fast, and does not require large multiplications of data space that easily go over the value of Max Int.

Additionally: To make the user-supplied arguments not care about order I am using a handy swap function I found in the wild in conjunction with my function below.

// swap function

function swap(&$a,&$b) { list($a,$b)=array($b,$a); } // swap 2 variables-- no temp variable needed!

// function to get a random value within a given range of integers

function get_secure_random_ranged_value($max=99, $min=0) // handles 1 or 2 arguments, order does not matter
{
$sortarray = array();
$lo = (int)$min;
$hi = (int)$max;
if ($lo > $hi) swap($lo,$hi);
$data_range = abs($hi - $lo) + 1; // +1 includes both the lowest 'zero' value and highest value of range
$bytes_per_key = 4; // Max: ffff hex = 4,294,967,296 dec (over 4 billion) -- large span of random values covers massive datasets
$num_bytes = $data_range * $bytes_per_key;
$byte_string = (bin2hex(openssl_random_pseudo_bytes($num_bytes))); // only one call needed to get string of bytes
$byte_blocksize = $bytes_per_key << 1; // shift multiply by 2 since a byte is 2 characters wide

while ($key = substr($byte_string,0,$byte_blocksize)) { // get next byte block from string
$byte_string = substr($byte_string,$byte_blocksize); // remove selected byte block from string
$sortarray[]=$key; // populate the array with keys temporarily as array values
}

$sortarray = array_flip($sortarray); // swap to use the byte values as keys
ksort($sortarray); // randomize by keys
return array_shift($sortarray) + $lo; // grab top value from array and add it to the lowest value in the range
}

//
// example getting values from 0 to 21:
//

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

//
// example getting values from 14 to 21:
//

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

//
// sample results from 14-21
//

-> result: 14
-> result: 18
-> result: 20
-> result: 15
-> result: 20
-> result: 16
-> result: 21
-> result: 15
-> result: 16
-> result: 17
up
4
acatalept at gmail
13 years ago
FYI, openssl_random_pseudo_bytes() can be incredibly slow under Windows, to the point of being unusable. It frequently times out (>30 seconds execution time) on several Windows machines of mine.

Apparently, it's a known problem with OpenSSL (not PHP specifically).

See: http://www.google.com/search?q=openssl_random_pseudo_bytes+slow
up
5
Tyler Larson
15 years ago
If you don't have this function but you do have OpenSSL installed, you can always fake it:

<?php
function openssl_random_pseudo_bytes($length) {
$length_n = (int) $length; // shell injection is no fun
$handle = popen("/usr/bin/openssl rand $length_n", "r");
$data = stream_get_contents($handle);
pclose($handle);
return
$data;
}
?>
up
-3
crrodriguez at opensuse dot org
13 years ago
Remember to request at very least 8 bytes of entropy, ideally 32 or 64, to avoid possible theorical bruteforce attacks.
up
-4
umairkhi at hotmail dot com
6 years ago
After the fix of insecure number generation here:

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

This function as well as the text here needs an update. I believe this function is safe to use in FIPS compliant apps as well as it now used RAND_bytes instead of the insecure RAND_pseudo_bytes().
To Top