PHP 中的隨機數——你覺得可靠么?

本文主要分析以加密為目的的隨機數生成問題人断。PHP 5 并未提供生成強加密隨機數的簡便機制,但是朝蜘,PHP 7 引入了兩個 CSPRNG 函數以解決該問題恶迈。系 OneAPM 工程師編譯整理。

PHP 中的隨機數——你覺得可靠么?

什么是 CSPRNG谱醇?

引用維基百科的定義暇仲,密碼安全的虛擬隨機數生成器(Cryptographically Secure Pseudorandom Number Generator,CSPRNG)是帶有特定屬性使之在密碼學中適用的虛擬隨機數生成器(pseudo-random number generator副渴,PRNG)奈附。

CSPRNG 主要用于:

  • 生成鍵(比如:生成復雜的鍵)
  • 為新的用戶賬號生成隨機密碼
  • 加密系統(tǒng)

保證高安全水準的一個重要因素便是高質量的隨機數。

PHP 7 中的 CSPRNG

PHP 7 為 CSPRNG 引入了兩種新函數:random_bytesrandom_int煮剧。

random_bytes 函數返回 string 類型斥滤,并接受一個 int 類型為參數,該參數規(guī)定了所返回字符串的字節(jié)長度勉盅。

例如:

$bytes = random_bytes('10');
var_dump(bin2hex($bytes));
//possible ouput: string(20) "7dfab0af960d359388e6"  

random_int 函數返回給定范圍內的整型數字佑颇。

舉例:

var_dump(random_int(1, 100));
//possible output: 27

幕后解密

以上函數的隨機數來源因環(huán)境不同而有所差異:

  • 在 Windows 系統(tǒng),會使用 CryptGenRandom() 函數草娜。
  • 在其他平臺挑胸,會優(yōu)先使用 arc4random_buf() 函數(限 BSD 衍生系統(tǒng)或帶 libbsd 的系統(tǒng))。
  • 若以上兩點均不符合宰闰,會使用 Linux [getrandom(2)](http://man7.org/linux/man-pages/man2/ getrandom.2.html) 系統(tǒng)調用。
  • 若以上來源均不符合移袍,會拋出 Error

一個簡例

一個好的隨機數生成系統(tǒng)能確保生成質量適合的隨機數咐容。為了檢驗質量,需要運行一系列的統(tǒng)計試驗。此處路狮,暫不深入討論復雜的統(tǒng)計話題虫啥,將已知的行為與隨機數生成器的結果進行比較奄妨,有助于質量評估。

一個簡單的測試方法是擲骰游戲砸抛。假設投擲一次评雌,投出6的概率是1/6。如果同時投擲三個骰子直焙,投100次景东,投得零次奔誓、一次、兩次及三次6的次數大概是:

  • 0 次6 = 57.9 次
  • 1 次6 = 34.7 次
  • 2 次6 = 6.9 次
  • 3 次6 = 0.5 次

以下是骰子投擲100萬次的代碼:

$times = 1000000;
$result = [];
for ($i=0; $i<$times; $i++){
    $dieRoll = array(6 => 0); //initializes just the six counting to zero
    $dieRoll[roll()] += 1; //first die
    $dieRoll[roll()] += 1; //second die
    $dieRoll[roll()] += 1; //third die
    $result[$dieRoll[6]] += 1; //counts the sixes
}
function roll(){
    return random_int(1,6);
}
var_dump($result);

用 PHP 7 的 random_int 與簡單的 rand 函數測試上面的代碼和措,可能會得到:

<table>
<thead>
<tr>
<th>Sixes</th>
<th>expected</th>
<th>random_int</th>
<th>rand</th>
</tr>
</thead>
<tbody><tr>
<td>0</td>
<td>579000</td>
<td>579430</td>
<td>578179</td>
</tr>
<tr>
<td>1</td>
<td>347000</td>
<td>346927</td>
<td>347620</td>
</tr>
<tr>
<td>2</td>
<td>69000</td>
<td>68985</td>
<td>69586</td>
</tr>
<tr>
<td>3</td>
<td>5000</td>
<td>4658</td>
<td>4615</td>
</tr>
</tbody></table>

更直觀地查看 randrandom_int 的差別蜕煌,可以運用方程式放大兩組結果的差異,并繪制成圖表:

php result - expected result / sqrt(expected)

得到的結果如下:

PHP 中的隨機數——你覺得可靠么?

(結果越接近零越好)

即便三個6的組合表現(xiàn)一般贫母,且該測試與真實應用相比太過簡單盒刚,我們也能清楚地看到 random_int 的表現(xiàn)優(yōu)于 rand。況且伪冰,隨機數生成器的可預見行為樟蠕、重復行為越少,應用的安全程度就更高吓懈。

PHP 5 又如何呢靡狞?

默認情況下,PHP 5 并未提供任何強虛擬隨機數生成器劝术。而實際使用中腮恩,可以使用 openssl_random_pseudo_bytes()温兼、mcrypt_create_iv() 方法秸滴,或直接結合使用 /dev/random/dev/urandomfread() 方法。此外荡含,還有包 RandomLiblibsodium届垫。

如果你想用一個比較好的隨機數生成器,同時能與 PHP 7 兼容误债,你可以使用 Paragon Initiative 公司的 random_compat 庫符衔。該庫允許在 PHP 5.x 項目中使用 random_bytes()random_int() 方法。

該庫可以使用 Composer 進行安裝:

composer require paragonie/random_compat
require 'vendor/autoload.php';
$string = random_bytes(32);
var_dump(bin2hex($string));
// string(64) "8757a27ce421b3b9363b7825104f8bc8cf27c4c3036573e5f0d4a91ad2aaec6f"
$int = random_int(0,255);
var_dump($int);
// int(81)

random_compat 庫使用了與 PHP 7 中不同的優(yōu)先序列:

  1. 如果可用躺盛,先使用 fread() /dev/urandom
  2. mcrypt_create_iv($bytes, MCRYPT_CREATE_IV)
  3. COM('CAPICOM.Utilities.1')->GetRandom()
  4. openssl_random_pseudo_bytes()

想了解為何采用這一優(yōu)先序列形帮,可以閱讀本文檔

使用該庫生成密碼的簡單案例如下:

$passwordChar = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$passwordLength = 8;
$max = strlen($passwordChar) - 1;
$password = '';
for ($i = 0; $i < $passwordLength; ++$i) {
    $password .= $passwordChar[random_int(0, $max)];
}
echo $password;
//possible output: 7rgG8GHu

總結

你應該盡量使用在密碼學上安全的虛擬隨機數生成器界斜。random_compat 庫為此提供了很好的實現(xiàn)方法合冀。

如果你想使用可靠的隨機數來源,正如前文所述君躺,盡快開始使用 random_intrandom_bytes 吧!

原文地址:http://www.sitepoint.com/randomness-php-feel-lucky/

OneAPM for PHP 能夠深入到所有 PHP 應用內部完成應用性能管理 能夠深入到所有 PHP 應用內部完成應用性能管理和監(jiān)控林螃,包括代碼級別性能問題的可見性俺泣、性能瓶頸的快速識別與追溯完残、真實用戶體驗監(jiān)控横漏、服務器監(jiān)控和端到端的應用性能管理。想閱讀更多技術文章铝宵,請訪問 OneAPM 官方技術博客华畏。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市侣夷,隨后出現(xiàn)的幾起案子仑乌,更是在濱河造成了極大的恐慌,老刑警劉巖晰甚,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厕九,死亡現(xiàn)場離奇詭異,居然都是意外死亡扁远,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門并闲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谷羞,“玉大人,你說我怎么就攤上這事购公⊙愀瑁” “怎么了知残?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵比庄,是天一觀的道長乏盐。 經常有香客問我,道長神凑,這世上最難降的妖魔是什么何吝? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮瓣喊,結果婚禮上黔酥,老公的妹妹穿的比我還像新娘。我一直安慰自己跪者,他們只是感情好,可當我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布逗概。 她就那樣靜靜地躺著柜蜈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪隶垮。 梳的紋絲不亂的頭發(fā)上秘噪,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天,我揣著相機與錄音蹋偏,去河邊找鬼至壤。 笑死,一個胖子當著我的面吹牛像街,可吹牛的內容都是我干的晋渺。 我是一名探鬼主播脓斩,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼随静,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了燎猛?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤鸠信,失蹤者是張志新(化名)和其女友劉穎论寨,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體绰垂,經...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡火焰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年昌简,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纯赎。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡犬金,死狀恐怖念恍,靈堂內的尸體忽然破棺而出晚顷,到底是詐尸還是另有隱情,我是刑警寧澤瞳氓,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布栓袖,位于F島的核電站锅锨,受9級特大地震影響恋沃,放射性物質發(fā)生泄漏必指。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一梅割、第九天 我趴在偏房一處隱蔽的房頂上張望葛家。 院中可真熱鬧,春花似錦癞谒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至逗物,卻和暖如春瑟俭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背尔当。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锐帜,地道東北人畜号。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像蛮拔,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子建炫,可洞房花燭夜當晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內容