PHP 中的安全随机数生成
Posted
技术标签:
【中文标题】PHP 中的安全随机数生成【英文标题】:Secure random number generation in PHP 【发布时间】:2010-11-14 00:46:26 【问题描述】:用例:“我忘记了密码”按钮。我们找不到用户的原始密码,因为它以散列形式存储,所以唯一要做的就是生成一个新的随机密码并通过电子邮件发送给他。这需要密码学上不可预测的随机数,而 mt_rand 不够好,通常我们不能假设托管服务将提供对操作系统的访问以安装密码随机数模块等,所以我正在寻找一种方法在 php 本身中生成安全的随机数。
到目前为止,我提出的解决方案包括存储一个初始种子,然后为每个调用存储,
result = seed
seed = sha512(seed . mt_rand())
这是基于 sha512 散列函数的安全性(mt_rand 调用只是为了让获取数据库副本的对手更加困难)。
是我遗漏了什么,还是有更好的解决方案?
【问题讨论】:
拦截电子邮件可能比尝试猜测有效的密码重置令牌更容易。 编辑:正如下面评论中所指出的,random.org 不推荐用于密码学。如果你觉得可以看看Random.org您的应用程序保证真正的随机数。他们有多种语言的客户端界面:http://www.random.org/clients/http/archive/ 仅供参考,发送新密码是非常糟糕的用户体验。向他们发送一个可以设置新密码的表单的链接。 crypto.stackexchange.com/questions/1619/… ***.com/a/31284266/2224584 【参考方案1】:我强烈建议将 unix 系统上的 /dev/urandom 或 windows 平台上的 crypto-api 作为密码的熵源。
我不能足够强调实现哈希不是神奇的熵增加设备的重要性。以这种方式滥用它们并不比在散列之前使用种子和 rand() 数据更安全,我相信你知道这不是一个好主意。种子抵消了(确定性 mt_rand()),因此即使包含它也没有任何意义。
人们认为他们很聪明很聪明,他们的劳动成果是脆弱的系统和设备,它们将他们系统的安全性和其他系统的安全性(通过糟糕的建议)置于不必要的危险之中。
两个错误不等于一个正确。一个系统的强大取决于它最薄弱的部分。这不是一个许可或借口,可以接受让它变得更加不安全。
这里是一些 PHP 代码,用于从 this comment at php.net by Mark Seecof 获取安全的随机 128 位字符串:
“如果您需要一些伪随机位用于安全或加密目的(例如,用于分组密码的随机 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 通常是安全的,尽管两者都会在对方平台上静默失败。把它们都放在那里,这样你的代码就更便携了。”
【讨论】:
这里有一些代码可以做到这一点:php.net/manual/en/function.mt-rand.php#83655 @Tom, Einstein:鉴于这个问题似乎是 Google 上“php 安全随机”的第一个结果,我觉得逐字引用代码示例是个好主意,以防万一链接有一天会断开。 更新:来自Windows Platform SDK Redistributable: CAPICOM - “CAPICOM 从适用于 Windows 7 的 Windows SDK 开始被排除在 Windows SDK 之外,尽管分发包将在 32 位版本的 Windows 7 和 Windows Server 2008 上运行R2 操作系统,不推荐使用。更多信息请参见Alternatives to Using CAPICOM" @Einstein,为什么不直接使用openssl_random_pseudo_bytes
?【参考方案2】:
您也可以考虑使用 OpenSSL openssl_random_pseudo_bytes,它从 PHP 5.3 开始可用。
string openssl_random_pseudo_bytes ( int $length [, bool &$crypto_strong ] )
生成一串伪随机字节,字节数由长度参数确定。 它还指示是否使用加密强算法来生成伪随机字节,并通过可选的 crypto_strong 参数执行此操作。这是 FALSE 的情况很少见,但某些系统可能已损坏或陈旧。
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
【讨论】:
这不安全,根据paragonie.com/blog/2015/07/… @DavidMeister 字面意思是“快速回答:如果您正在开发 Web 应用程序并且您的项目需要一个简单且安全的解决方案来生成随机整数或字符串,只需使用 random_bytes()”【参考方案3】: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。
【讨论】:
以上是关于PHP 中的安全随机数生成的主要内容,如果未能解决你的问题,请参考以下文章