在 PHP 中安全生成随机数

2022-08-30 12:08:25

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

到目前为止,我想出的解决方案包括存储一个初始种子,然后对于每个调用,

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

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

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


答案 1

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

我再怎么强调也不过分,意识到哈希值不是神奇的熵增加设备的重要性。以这种方式滥用它们并不比在散列之前使用种子和rand()数据更安全,我相信你认识到这不是一个好主意。种子会抵消(确定性mt_rand()),因此即使包含它也没有意义。

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

两个错误不能成为一个正确。一个系统的强大程度取决于其最薄弱的部分。这不是接受使它更加不安全的许可或借口。


以下是一些PHP代码,用于获取安全的随机128位字符串,来自Mark Seecof在 php.net 的注释

“如果你需要一些伪随机位用于安全或加密目的(例如,随机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 的尝试通常是安全的,尽管每个尝试都会在另一个平台上以静默方式失败。把它们都留在那里,这样你的代码就会更有便携性。


答案 2

你也可以考虑使用OpenSSL openssl_random_pseudo_bytes,它从PHP 5.3开始就可用了。

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

生成伪随机字节字符串,其字节数由 length 参数确定。它还指示是否使用了加密很强的算法来生成伪随机字节,并通过可选的 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


推荐