本文主要分析以加密為目的的隨機數生成問題人断。PHP 5 并未提供生成強加密隨機數的簡便機制,但是朝蜘,PHP 7 引入了兩個 CSPRNG 函數以解決該問題恶迈。系 OneAPM 工程師編譯整理。
什么是 CSPRNG谱醇?
引用維基百科的定義暇仲,密碼安全的虛擬隨機數生成器(Cryptographically Secure Pseudorandom Number Generator,CSPRNG)是帶有特定屬性使之在密碼學中適用的虛擬隨機數生成器(pseudo-random number generator副渴,PRNG)奈附。
CSPRNG 主要用于:
- 生成鍵(比如:生成復雜的鍵)
- 為新的用戶賬號生成隨機密碼
- 加密系統(tǒng)
保證高安全水準的一個重要因素便是高質量的隨機數。
PHP 7 中的 CSPRNG
PHP 7 為 CSPRNG 引入了兩種新函數:random_bytes
與 random_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>
更直觀地查看 rand
與 random_int
的差別蜕煌,可以運用方程式放大兩組結果的差異,并繪制成圖表:
php result - expected result / sqrt(expected)
得到的結果如下:
(結果越接近零越好)
即便三個6的組合表現(xiàn)一般贫母,且該測試與真實應用相比太過簡單盒刚,我們也能清楚地看到 random_int
的表現(xiàn)優(yōu)于 rand
。況且伪冰,隨機數生成器的可預見行為樟蠕、重復行為越少,應用的安全程度就更高吓懈。
PHP 5 又如何呢靡狞?
默認情況下,PHP 5 并未提供任何強虛擬隨機數生成器劝术。而實際使用中腮恩,可以使用 openssl_random_pseudo_bytes()
温兼、mcrypt_create_iv()
方法秸滴,或直接結合使用 /dev/random
或 /dev/urandom
與 fread()
方法。此外荡含,還有包 RandomLib 或 libsodium届垫。
如果你想用一個比較好的隨機數生成器,同時能與 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)先序列:
- 如果可用躺盛,先使用 fread() /dev/urandom
- mcrypt_create_iv($bytes, MCRYPT_CREATE_IV)
- COM('CAPICOM.Utilities.1')->GetRandom()
- 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_int
與 random_bytes
吧!
原文地址:http://www.sitepoint.com/randomness-php-feel-lucky/
OneAPM for PHP 能夠深入到所有 PHP 應用內部完成應用性能管理 能夠深入到所有 PHP 應用內部完成應用性能管理和監(jiān)控林螃,包括代碼級別性能問題的可見性俺泣、性能瓶頸的快速識別與追溯完残、真實用戶體驗監(jiān)控横漏、服務器監(jiān)控和端到端的應用性能管理。想閱讀更多技術文章铝宵,請訪問 OneAPM 官方技術博客华畏。