在PHP中保护随机数生成

时间:2022-12-13 12:35:21

Use case: the "I forgot my password" button. We can't find the user's original password because it's stored in hashed form, so the only thing to do is generate a new random password and e-mail it to him. This requires cryptographically unpredictable random numbers, for which mt_rand is not good enough, and in general we can't assume a hosting service will provide access to the operating system to install a cryptographic random number module etc. so I'm looking for a way to generate secure random numbers in PHP itself.

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

The solution I've come up with so far involves storing an initial seed, then for each call,

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

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

This is based on the security of the sha512 hash function (the mt_rand call is just to make life a little more difficult for an adversary who obtains a copy of the database).

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

Am I missing something, or are there better known solutions?

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

3 个解决方案

#1


36  

You can also consider using OpenSSL openssl_random_pseudo_bytes, it's available since PHP 5.3.

您还可以考虑使用OpenSSL openssl_random_pseudo_bytes,它自PHP 5.3起可用。

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

Generates a string of pseudo-random bytes, with the number of bytes determined by the length parameter. It also indicates if a cryptographically strong algorithm was used to produce the pseudo-random bytes, and does this via the optional crypto_strong parameter. It's rare for this to be FALSE, but some systems may be broken or old.

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

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

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

Since PHP 7 there is also random_bytes function available

从PHP 7开始,还有random_bytes函数可用

string random_bytes ( int $length )

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

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

#2


58  

I strongly recommend targeting /dev/urandom on unix systems or the crypto-api on the windows platform as an entropy source for passwords.

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

I can't stress enough the importance of realizing hashes are NOT magical entropy increasing devices. Misusing them in this manner is no more secure than using the seed and rand() data before it had been hashed and I'm sure you recognize that is not a good idea. The seed cancels out (deterministic mt_rand()) and so there is no point at all in even including it.

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

People think they are being smart and clever and the result of their labor are fragile systems and devices which put the security of their systems and the security of other systems (via poor advice) in unecessary jeopardy.

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

Two wrongs don't make a right. A system is only as strong as its weakest part. This is not a license or excuse to accept making even more of it insecure.

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


Here is some PHP code to obtain a secure random 128-bit string, from this comment at php.net by Mark Seecof:

下面是一些获取安全随机128位字符串的PHP代码,来自php.net的Mark Seecof评论:

"If you need some pseudorandom bits for security or cryptographic purposes (e.g.g., random IV for block cipher, random salt for password hash) mt_rand() is a poor source. On most Unix/Linux and/or MS-Windows platforms you can get a better grade of pseudorandom bits from the OS or system library, like this:

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

<?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
}
?>

NB: it is generally safe to leave both the attempt to read /dev/urandom and the attempt to access CAPICOM in your code, though each will fail silently on the other's platform. Leave them both there so your code will be more portable."

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

#3


6  

PHP ships with a new set of CSPRNG functions (random_bytes() and random_int()). It's trivial to turn the latter function into a string generator function:

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;
}

If you need to use this in a PHP 5 project, feel free to grab a copy of random_compat, which is a polyfill for these functions.

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

#1


36  

You can also consider using OpenSSL openssl_random_pseudo_bytes, it's available since PHP 5.3.

您还可以考虑使用OpenSSL openssl_random_pseudo_bytes,它自PHP 5.3起可用。

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

Generates a string of pseudo-random bytes, with the number of bytes determined by the length parameter. It also indicates if a cryptographically strong algorithm was used to produce the pseudo-random bytes, and does this via the optional crypto_strong parameter. It's rare for this to be FALSE, but some systems may be broken or old.

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

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

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

Since PHP 7 there is also random_bytes function available

从PHP 7开始,还有random_bytes函数可用

string random_bytes ( int $length )

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

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

#2


58  

I strongly recommend targeting /dev/urandom on unix systems or the crypto-api on the windows platform as an entropy source for passwords.

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

I can't stress enough the importance of realizing hashes are NOT magical entropy increasing devices. Misusing them in this manner is no more secure than using the seed and rand() data before it had been hashed and I'm sure you recognize that is not a good idea. The seed cancels out (deterministic mt_rand()) and so there is no point at all in even including it.

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

People think they are being smart and clever and the result of their labor are fragile systems and devices which put the security of their systems and the security of other systems (via poor advice) in unecessary jeopardy.

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

Two wrongs don't make a right. A system is only as strong as its weakest part. This is not a license or excuse to accept making even more of it insecure.

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


Here is some PHP code to obtain a secure random 128-bit string, from this comment at php.net by Mark Seecof:

下面是一些获取安全随机128位字符串的PHP代码,来自php.net的Mark Seecof评论:

"If you need some pseudorandom bits for security or cryptographic purposes (e.g.g., random IV for block cipher, random salt for password hash) mt_rand() is a poor source. On most Unix/Linux and/or MS-Windows platforms you can get a better grade of pseudorandom bits from the OS or system library, like this:

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

<?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
}
?>

NB: it is generally safe to leave both the attempt to read /dev/urandom and the attempt to access CAPICOM in your code, though each will fail silently on the other's platform. Leave them both there so your code will be more portable."

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

#3


6  

PHP ships with a new set of CSPRNG functions (random_bytes() and random_int()). It's trivial to turn the latter function into a string generator function:

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;
}

If you need to use this in a PHP 5 project, feel free to grab a copy of random_compat, which is a polyfill for these functions.

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