【问题标题】:replace rand() with openssl_random_pseudo_bytes()用 openssl_random_pseudo_bytes() 替换 rand()
【发布时间】:2010-11-21 17:50:45
【问题描述】:

我需要替换 PHP 的 rand() 函数,该函数使用强加密随机数生成器。

openssl_random_pseudo_bytes() 函数可让您访问强随机数生成器,但它会将其数据输出为字节字符串。相反,我需要一个介于 0 和 X 之间的整数。

我想关键是将openssl_random_pseudo_bytes() 的输出转换为整数,然后您可以对其进行任何您需要的数学运算。我可以想到一些从字节字符串转换为整数的“蛮力”方法,但我希望得到一些……优雅的东西。

【问题讨论】:

  • mt_rand() 会提供比 rand() 质量更好的随机数,但它的密码强度也不高。
  • 小心,这个答案是错误的。更具体地说,“% $range”部分不正确。采取以下场景:您想要生成 1(包括)和 4(不包括)之间的数字(因为您执行 max-min)。 $range = 3。现在 3 不会平均分为 2^8,这意味着 1 将比 3 更常见。要非常小心,你想制作一个安全的随机数生成器,但无意中让它变得不安全,这就是为什么建议使用现有的实现。
  • @chacham15:如果理解正确,在 8 位范围内,您会得到 85 组 [1,2,3] 加上一个 [1,] 剩余,大约 1.17% 为“1”比“2”或“3”更有可能出现,对吧?你将如何解决这个问题?显然,使用现有实现不是一种选择,否则我不会问这个问题。
  • $num = hexdec(bin2hex(openssl_random_pseudo_bytes($length,$s))); $max = pow(2, 8*length); //for clarity, you can replace with a faster bit shift $remainder = $max % $range; $max = $max - $remainder; if ($num > $max){ return crypto_rand($min,$max); } return $min + ($num % $range);
  • 换句话说,你基本上得到了平均划分到你的范围内的最高数字(这也在随机数生成器的范围内),如果该值高于你必须重新运行该函数。

标签: php random cryptography


【解决方案1】:

openssl_random_pseudo_bytes() 的手册页有一个我认为您想要的示例。您可以在openssl_random_pseudo_bytes() 的输出上调用bin2hex() 以转换为十六进制数,然后在该值上调用hexdec() 以转换为十进制数。

$rand_num = hexdec(bin2hex(openssl_random_pseudo_bytes($length, $strong)));

此时,您可以进行任何数学运算,以获得所需范围内的值。您可能拥有的另一个(作弊者)选项是运行系统命令来生成随机数 - 对于在线提供的各种操作系统的随机数生成器,有一些不错的选项。

【讨论】:

  • 这并不能完全回答问题,请参阅下面的其他答案。
【解决方案2】:

好吧,只需对 openssl_random_pseudo_bytes 的结果使用 hexdec,您就会得到整数。它就像它得到的一样优雅:)

print hexdec('45261b8f');

>  1160125327

【讨论】:

  • 不回答 ops 问题,因为 rand 需要一个最小值和最大值,这只是返回一个随机数。
  • 不是直接添加,但是添加它相当简单——从操作员自己提供的解决方案中可以看出。
【解决方案3】:

这是上述解决方案的一个版本,它不使用递归函数调用:

function secure_rand($min,$max) {
    $range = $max - $min + 1;
    if ($range == 0) return $min;
    $length = (int) (log($range,2) / 8) + 1;
    $max = pow(2, 8 * $length);
    $num = $max + 1; // Hackish, I know..
    while ($num > $max) {
        $num = hexdec(bin2hex(openssl_random_pseudo_bytes($length,$s)));
    }
    return ($num  % $range) + $min;
}

【讨论】:

    【解决方案4】:

    使用提供的建议,我使用 OpenSSL 创建了一个替代 rand() 的插件。我会把它包括在这里以供后代使用。

    $pedantic 选项通过在结果不会在可能的范围内均匀分布时重新开始来提供无偏差的结果。

    function crypto_rand($min,$max,$pedantic=True) {
        $diff = $max - $min;
        if ($diff <= 0) return $min; // not so random...
        $range = $diff + 1; // because $max is inclusive
        $bits = ceil(log(($range),2));
        $bytes = ceil($bits/8.0);
        $bits_max = 1 << $bits;
        // e.g. if $range = 3000 (bin: 101110111000)
        //  +--------+--------+
        //  |....1011|10111000|
        //  +--------+--------+
        //  bits=12, bytes=2, bits_max=2^12=4096
        $num = 0;
        do {
            $num = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes))) % $bits_max;
            if ($num >= $range) {
                if ($pedantic) continue; // start over instead of accepting bias
                // else
                $num = $num % $range;  // to hell with security
            }
            break;
        } while (True);  // because goto attracts velociraptors
        return $num + $min;
    }
    

    【讨论】:

    • 我添加了一个答案,其中包含有关如何使用 CryptoLib 的指南,以便将其作为替代品。这更安全(并且代码更高效),并且还提供了可重复性检查器。
    • @mjsa 虽然我确信您的加密库很棒,但这个问题是关于代码本身的机制,而不是要求某人构建另一个库来完成它。
    • 这不是一个糟糕的解决方案。我正在帮助将 PHP 7 的 random_int() 反向移植到 PHP 5 项目中,即使在 $max - $min &gt; PHP_INT_MAX 时我们的方法也有效。如果您想发表自己的看法,可以在 random_compat 下在 Github 上找到我们所做的工作。
    【解决方案5】:
    function ($min,$max){
        $range = $max-$min+1;
        do{
            $result = floor($range*(hexdec(bin2hex(openssl_random_pseudo_bytes(4)))/0xffffffff));
        } while($result == $range);    
        return $result + $min;
    }
    

    【讨论】:

      【解决方案6】:

      执行此操作的最简单方法(也是此处所有选项中最安全的方法)是使用 CryptoLib,它有一个 randomInt 函数,可以替代 rand。

      首先从 https://github.com/IcyApril/CryptoLib 下载 CryptoLib 并将其粘贴到您的项目中

      二,放入这段代码。将 path/to/ 替换为 cryptolib.php 的目录,将 min max 替换为您的最小和最大数字:

      <?php
        require_once('path/to/cryptoLib.php');
      
        $min = 1;
        $max = 5;
      
        $randomNum = CryptoLib::randomInt($min, $max);
      ?>
      

      CryptoLib 完整文档位于:https://cryptolib.ju.je/

      【讨论】:

        【解决方案7】:

        由于 PHP 7 现已推出,解决此问题的最简单方法是将所有 mt_rand 实例替换为 random_int

        (假设你已经升级了。)

        【讨论】:

          猜你喜欢
          • 2012-08-27
          • 2015-04-07
          • 2011-07-29
          • 2020-12-12
          • 1970-01-01
          • 2014-10-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多