正如我之前所说,我的 rand 函数是 phpseclib 的 crypt_random 函数的修改版本。你可以在我第一篇文章的链接中看到它。至少 phpseclib 密码库的作者证实了这一点;普通应用还不够?我不是在谈论极端/理论上的安全性,只是在真正需要的范围内谈论实际安全性,同时“轻松”/“足够低成本”可用于几乎所有网络上的普通应用程序。
在最坏的情况下(没有可用的 openssl_random_pseudo_bytes 或 urandom),phpseclib 的 crypt_random 有效且默默地回退到 mt_rand (你应该知道它真的很弱),但我的函数在这种情况下使用了更安全的方案。这只是一种方案的回退,即暴力破解/预测其输出要困难得多,并且(应该)在实践中足以满足所有普通应用程序/网站的需求。它使用了可能(在实践中非常有可能且难以预测/规避)随着时间的推移收集的额外熵,这对于外人来说很快变得几乎不可能知道。它将这种可能的熵添加到 mt_rand 的输出(以及其他源的输出:urandom、openssl_random_pseudo_bytes、mcrypt_create_iv)。如果你被告知你应该知道,这个熵可以被添加但不能被减去。在(几乎可以肯定非常罕见)最坏的情况下,额外的熵将是 0 或一些太小的量。在我认为几乎所有情况的平庸案例中,我认为这甚至比实际需要的还要多。(我有大量的密码学研究,所以当我说我认为时,它是基于比普通程序员更明智和科学的分析)。
查看我修改后的 crypt_random 的完整代码:
function crypt_random($min = 0, $max = 0x7FFFFFFF)
{
if ($min == $max) {
return $min;
}
global $entropy;
if (function_exists('openssl_random_pseudo_bytes')) {
// openssl_random_pseudo_bytes() is slow on windows per the following:
// http://stackoverflow.com/questions/1940168/openssl-random-pseudo-bytes-is-slow-php
if ((PHP_OS & "\xDF\xDF\xDF") !== 'WIN') { // PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster
extract(unpack('Nrandom', pack('H*', sha1(openssl_random_pseudo_bytes(4).$entropy.microtime()))));
return abs($random) % ($max - $min) + $min;
}
}
// see http://en.wikipedia.org/wiki//dev/random
static $urandom = true;
if ($urandom === true) {
// Warning's will be output unles the error suppression operator is used. Errors such as
// "open_basedir restriction in effect", "Permission denied", "No such file or directory", etc.
$urandom = @fopen('/dev/urandom', 'rb');
}
if (!is_bool($urandom)) {
extract(unpack('Nrandom', pack('H*', sha1(fread($urandom, 4).$entropy.microtime()))));
// say $min = 0 and $max = 3. if we didn't do abs() then we could have stuff like this:
// -4 % 3 + 0 = -1, even though -1 < $min
return abs($random) % ($max - $min) + $min;
}
if(function_exists('mcrypt_create_iv') and version_compare(PHP_VERSION, '5.3.0', '>=')) {
@$tmp16=mcrypt_create_iv(4, MCRYPT_DEV_URANDOM);
if($tmp16!==false) {
extract(unpack('Nrandom', pack('H*', sha1($tmp16.$entropy.microtime()))));
return abs($random) % ($max - $min) + $min;
}
}
/* Prior to PHP 4.2.0, mt_srand() had to be called before mt_rand() could be called.
Prior to PHP 5.2.6, mt_rand()'s automatic seeding was subpar, as elaborated here:
http://www.suspekt.org/2008/08/17/mt_srand-and-not-so-random-numbers/
The seeding routine is pretty much ripped from PHP's own internal GENERATE_SEED() macro:
http://svn.php.net/viewvc/php/php-src/tags/php_5_3_2/ext/standard/php_rand.h?view=markup */
static $seeded;
if (!isset($seeded) and version_compare(PHP_VERSION, '5.2.5', '<=')) {
$seeded = true;
mt_srand(fmod(time() * getmypid(), 0x7FFFFFFF) ^ fmod(1000000 * lcg_value(), 0x7FFFFFFF));
}
extract(unpack('Nrandom', pack('H*', sha1(mt_rand(0, 0x7FFFFFFF).$entropy.microtime()))));
return abs($random) % ($max - $min) + $min;
}
$entropy 包含我的额外熵,它来自所有请求参数的熵组合到现在 + 当前请求的参数熵 + 在安装时手动设置的随机字符串 (*) 的熵。
*:长度:22,由大小写字母+数字组成(熵大于128位)