在PHP中保护随机数生成

时间:2009-07-25 17:35:18

标签: php security hash random

用例:“我忘了密码”按钮。我们找不到用户的原始密码,因为它以散列形式存储,因此唯一要做的就是生成一个新的随机密码并通过电子邮件发送给他。这需要加密不可预测的随机数,mt_rand不够好,一般我们不能假设托管服务将提供对操作系统的访问以安装加密随机数模块等所以我正在寻找一种方法用PHP生成安全的随机数。

到目前为止我提出的解决方案涉及存储初始种子,然后是每次调用,

result = seed
seed = sha512(seed . mt_rand())

这是基于sha512哈希函数的安全性(mt_rand调用只是为了获取数据库副本的对手的生活有点困难。)

我错过了什么,还是有更好的解决方案?

3 个答案:

答案 0 :(得分:59)

我强烈建议在unix系统上使用/ dev / urandom或在Windows平台上使用crypto-api作为密码的熵源。

我无法强调实现哈希的重要性不是神奇的增加熵的设备。以这种方式滥用它们并不比使用种子和rand()数据在散列之前更安全,我确信你认识到这不是一个好主意。种子取消了(确定性的mt_rand()),所以即使包括它也没有任何意义。

人们认为他们聪明聪明,他们的劳动成果是脆弱的系统和设备,使他们的系统安全和其他系统的安全(通过糟糕的建议)处于不必要的危险之中。

两个错误并不是正确的。一个系统只有它最薄弱的部分一样强大。这不是接受使其更加不安全的许可或借口。


以下是一些PHP代码,用于从this comment at php.net by Mark Seecof获取安全的随机128位字符串:

  

“如果你需要一些伪随机位用于安全或加密目的(egg,随机IV用于分组密码,随机盐用于密码散列)mt_rand()是一个很差的来源。在大多数Unix / Linux和/或MS-Windows平台上您可以从操作系统或系统库中获得更好等级的伪随机位,如下所示:

     
<?php
// get 128 pseudorandom bits in a string of 16 bytes

$pr_bits = '';

// Unix/Linux platform?
$fp = @fopen('/dev/urandom','rb');
if ($fp !== FALSE) {
    $pr_bits .= @fread($fp,16);
    @fclose($fp);
}

// MS-Windows platform?
if (@class_exists('COM')) {
    // http://msdn.microsoft.com/en-us/library/aa388176(VS.85).aspx
    try {
        $CAPI_Util = new COM('CAPICOM.Utilities.1');
        $pr_bits .= $CAPI_Util->GetRandom(16,0);

        // if we ask for binary data PHP munges it, so we
        // request base64 return value.  We squeeze out the
        // redundancy and useless ==CRLF by hashing...
        if ($pr_bits) { $pr_bits = md5($pr_bits,TRUE); }
    } catch (Exception $ex) {
        // echo 'Exception: ' . $ex->getMessage();
    }
}

if (strlen($pr_bits) < 16) {
    // do something to warn system owner that
    // pseudorandom generator is missing
}
?>
     

注意:保留读取/ dev / urandom的尝试和尝试访问代码中的CAPICOM通常是安全的,尽管每个都会在另一个平台上无声地失败。把它们留在那里,这样你的代码就会更便携。“

答案 1 :(得分:38)

您还可以考虑使用OpenSSL openssl_random_pseudo_bytes,它自PHP 5.3开始提供。

 string openssl_random_pseudo_bytes ( int $length [, bool &$crypto_strong ] )
  

生成一串伪随机字节,其长度参数确定的字节数。   它还指示是否使用加密强算法来生成伪随机字节,并通过可选的crypto_strong参数执行此操作。这种情况很少见,但有些系统可能会损坏或过时。

http://www.php.net/manual/en/function.openssl-random-pseudo-bytes.php

自PHP 7起,还有random_bytes函数可用

string random_bytes ( int $length )

http://php.net/manual/en/function.random-bytes.php

答案 2 :(得分:6)

PHP附带了一组新的CSPRNG函数(random_bytes()random_int())。将后一个函数转换为字符串生成器函数是非常简单的:

<?php
/**
 * Generate a random string, using a cryptographically secure 
 * pseudorandom number generator (random_int)
 * 
 * For PHP 7, random_int is a PHP core function
 * For PHP 5.x, depends on https://github.com/paragonie/random_compat
 * 
 * @param int $length      How many characters do we want?
 * @param string $keyspace A string of all possible characters
 *                         to select from
 * @return string
 */
function random_str(
    $length,
    $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
) {
    $str = '';
    $max = mb_strlen($keyspace, '8bit') - 1;
    if ($max < 1) {
        throw new Exception('$keyspace must be at least two characters long');
    }
    for ($i = 0; $i < $length; ++$i) {
        $str .= $keyspace[random_int(0, $max)];
    }
    return $str;
}

如果你需要在PHP 5项目中使用它,可以随意获取random_compat的副本,这是这些函数的polyfill。