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(整數 $length, 布林值 &$strong_result = null): 字串

產生一個偽隨機位元組的字串,位元組數由 length 參數決定。

它還會透過可選的 strong_result 參數指示是否使用了密碼學強度高的演算法來產生偽隨機位元組。這個參數很少是 false,但某些系統可能已損壞或過時。

參數

length

所需位元組字串的長度。必須是小於或等於 2147483647 的正整數。PHP 會嘗試將此參數轉換為非 null 的整數來使用它。

strong_result

如果傳入函式,這將保存一個 布林值,用於判斷所使用的演算法是否為「密碼學強度高」,例如,是否安全地用於 GPG、密碼等。如果是,則為 true,否則為 false

傳回值

傳回產生的字串

錯誤/例外

openssl_random_pseudo_bytes() 失敗時會拋出 例外

更新日誌

版本 說明
8.0.0 strong_result 現在可以為 null。
7.4.0 該函式在失敗時不再傳回 false,而是拋出 例外

範例

範例 #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)

另請參閱

  • random_bytes() - 取得加密安全的隨機位元組
  • bin2hex() - 將二進制數據轉換為十六進制表示法
  • crypt() - 單向字串雜湊
  • random_int() - 取得加密安全的、均勻分佈的隨機整數
新增註解

使用者貢獻的註解 8 則註解

nahun@telemako
11 年前
以下範例以圖像顯示隨機數的分佈。感謝 Hayley Watson 在 mt_rand 頁面上提供的 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);
?>
powtac at gmx dot 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 中的對應原始碼
christophe dot weis at statec dot etat dot lu
13 年前
另一個使用 OpenSSL 取代 rand() 的方法。

請注意,使用模數運算子 (%) 截斷結果的解決方案並非加密安全,因為生成的數字分佈不均勻,也就是說,某些數字可能出現的頻率比其他數字更高。

比使用模數運算子更好的解決方案是,如果結果太大則捨棄結果並產生一個新的結果。

<?php
function crypto_rand_secure($min, $max) {
$range = $max - $min;
if (
$range == 0) return $min; // 如果範圍為 0,則不那麼隨機…
$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;
}
?>
mailjeffclayton [at] gmail
5 年前
從給定範圍中取得均勻分佈的整數值

我建立這個函式是為了要解決使用模數運算造成範圍結果重疊(導致分佈不均)的問題。

對於不熟悉這個問題的人,我的意思是:

使用位元組作為 256 進位(16 進位)的基底,並嘗試在例如 10-20(範圍為 11)的數值範圍內尋找一個值,將無法均勻分割,因此使用模數運算的值會重疊,並使某些數字比其他數字更容易出現。

我不是根據位元組值來計算,而是使用位元組值作為排序的鍵值。 這個方法非常快速,而且不需要大量乘法運算,避免資料空間輕易超過最大整數值。

此外:為了讓使用者提供的參數順序無關,我將我找到的一個方便的交換函式與我下面的函式結合使用。

// 交換函式

function swap(&$a,&$b) { list($a,$b)=array($b,$a); } // 交換兩個變數 -- 不需要暫存變數!

// 函式:取得給定整數範圍內的隨機值

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
acatalept at gmail
13 年前
僅供參考,openssl_random_pseudo_bytes() 在 Windows 環境下可能會非常慢,甚至到無法使用的程度。它在我的幾台 Windows 電腦上經常逾時(執行時間超過 30 秒)。

顯然,這是 OpenSSL 的一個已知問題(並非 PHP 特有的)。

請參閱:http://www.google.com/search?q=openssl_random_pseudo_bytes+slow
Tyler Larson
15 年前
如果您沒有這個函式,但您安裝了 OpenSSL,您可以隨時模擬它

<?php
function openssl_random_pseudo_bytes($length) {
$length_n = (int) $length; // 避免 shell injection 的樂趣
$handle = popen("/usr/bin/openssl rand $length_n", "r");
$data = stream_get_contents($handle);
pclose($handle);
return
$data;
}
?>
crrodriguez at opensuse dot org
13 年前
請記得至少請求 8 位元組的熵,理想情況下是 32 或 64 位元組,以避免可能的理論蠻力攻擊。
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