在php中生成加密安全的随机数

时间:2009-06-24 23:39:51

标签: php security random

PHP的rand()函数不能提供良好的随机数。所以我开始使用mt_rand(),据说可以提供更好的结果。但这些结果有多好?有没有什么方法可以再次改善它们?

我的想法:

function rand_best($min, $max) {
    $generated = array();
    for ($i = 0; $i < 100; $i++) {
        $generated[] = mt_rand($min, $max);
    }
    shuffle($generated);
    $position = mt_rand(0, 99);
    return $generated[$position];
}

这应该给你“完美”的随机数,不应该吗?

16 个答案:

答案 0 :(得分:32)

Pseudorandom number generators(PRNG)是非常复杂的野兽。

没有真正的“完美”随机数生成器 - 事实上,从数学函数中可以做到的最好的是伪随机 - 它们似乎足够随意用于大多数意图和目的。

事实上,从PRNG返回的数字执行任何其他操作并不会真正增加其随机性,事实上,这个数字可能会变得不那么随机。

所以,我最好的建议是,不要搞乱从PRNG返回的值。使用足以满足预期用途的PRNG,如果不是,则找到可以产生更好结果的PRNG。

坦率地说,似乎mt_rand函数使用了Mersenne twister,这是一个相当不错的PRNG,所以对于大多数偶然使用来说它可能已经足够好了。

但是, Mersenne Twister并非设计用于任何安全环境。有关在需要随机性以确保安全性时使用的解决方案,请参阅this answer

修改

在评论中有一个问题,为什么对随机数执行操作可以减少随机数。例如,一些PRNG可以在比特的不同部分返回更一致,更少随机的数字 - 高端可以比低端更随机。

因此,在丢弃高端且返回低端的操作中,该值可以变得比从PRNG返回的原始值更少随机。

目前我找不到一个好的解释,但我是基于Random.nextInt(int)方法的Java文档,它是为了在指定范围内创建一个相当随机的值。该方法考虑了值的各部分的随机性差异,因此与更rand() % range等更天真的实现相比,它可以返回更好的随机数。

答案 1 :(得分:22)

快速回答:

在新的PHP7中,最终支持cryptographically secure pseudo-random integers.

int random_int ( int $min , int $max )

还有polyfill for PHP5x

更长的答案

没有完美的随机数生成器,计算机使用pseudorandom number generator创建看起来随机的序列。序列看起来是随机的(并传递了一些randomness tests),但由于有一些算法可以生成它,你可以用完全相同的状态重复算法并得到相同的结果。

cryptography“不发明自己的密码”相同的建议可以转换为随机数生成器,这意味着你不能只是将大量随机数生成器组合在一起并期望得到一个更好的发电机。

随机数生成器的一个子集是cryptographically secure random number generators

  

普通PRNG的要求也由a满足   密码保护PRNG,但反之则不然。 CSPRNG   要求分为两组:第一,他们通过统计   随机性测试;其次,他们在严肃的情况下保持良好状态   攻击,即使它们的初始或运行状态的一部分变为   可供攻击者使用

所以这非常接近你对“完美”的定义。在没有条件的情况下再次(除了学习如何进行加密之外),您应该尝试实现其中一种算法并在系统中使用它。

但幸运的是PHP7已实施,

int random_int ( int $min , int $max )
  

生成适合使用的加密随机整数   无偏见的结果至关重要(即改组扑克牌)。

随机来源如下:

  • 在Windows上,CryptGenRandom()专门用于
  • 如果arc4random_buf()可用(通常是BSD特定的)
  • ,则使用它
  • / dev / arandom用于可用的
  • getrandom(2)系统调用(在较新的Linux内核上)
  • / dev / urandom用于以上都不可用的地方

这使得之前的所有答案都已过时(有些已弃用)。

答案 2 :(得分:17)

我不确定你所做的“改善了”随机性。根据我的理解,您可以生成100个随机数,然后随机选择其中一个。

从我记忆中的概率过程来看,这可能不会增加随机性,就好像生成器函数中存在潜在的偏差(mt_rand()),然后它仍会以某种方式反映在输出中。

答案 3 :(得分:13)

mt_rand()以什么方式“坏”?

例如:如果有利于某个数字。让我们说mt_rand(1,10)有利于该范围内的低数字,即“1”和“2”平均每次超过10%。那么你的“改进”仍然会遇到同样的问题。

从故障序列中选择一个随机数仍然有问题。

答案 4 :(得分:12)

<?php
  function random_number(){
      return 4; // return generated number
                // guaranteed to be random
  }
  ?>

除了开玩笑之外,你正在进入一个关于什么是“随机”或什么是“最佳”的哲学问题。理想情况下,您希望随机数在整个过程中几乎没有模式。通常系统时间用作种子,但我也使用先前的随机数作为种子,之前的随机数字作为种子。问题是,凭借足够强大的计算机以及对硬件运行和生成器功能的全面了解,您将能够预测生成的整个数字集。因此,如果你有足够强大的计算机(有些人将上帝置于这个类别中)知道宇宙的所有可能的变量和功能,那么你就能够预测发生或将要发生的每一个事件。大多数随机数生成器本身都很好,但如果你知道有人可以看到这些模式,那么他们更像是美丽心灵中的那个人,你应该让他们进入诊所。

By popular demand:D

答案 5 :(得分:5)

我写了一个cronjob,定期从random.org获取1000个数字(比如每小时一次)并将它们添加到PHP数组中。每当我想在我的脚本中使用随机数时,我会使用mt_rand(0,1000)从中调用一个数字。一些额外的微秒开销,但我得到的是基于自然大气噪声的真正随机数。

答案 6 :(得分:2)

这一切都取决于你需要的随机数:) 对我来说ShuffleBag是最好的一个:)

答案 7 :(得分:2)

编辑:我的评论不再有效。请参阅以下答案:https://stackoverflow.com/a/31443898/109561


我猜你担心mt_rand()的分布。我测试了它,它非常水平,两个界限都是包容性的。

我在php手册中将我的测试添加到了mt_rand()文档的注释中,但由于政治太长,无法进入这里,所以它被一个愚蠢的主持人删除了。

答案 8 :(得分:1)

没有“完美”的随机数。无论你有什么主观的“完美”定义。你只能实现伪随机。

我只是想指出你正确的方向。你问了一个关于完美随机数的问题,即使是完美的引号。是的,你可以改善随机性。你甚至可以实现启发式或“自然”算法,比如“大气噪声”这样的想法 - 但你仍然不是完美的,不是任何手段。

答案 9 :(得分:1)

如果您不喜欢PHP内置的rand(),您可能不应该使用内置的shuffle(),因为它似乎是建立在rand()上的。< / p>

我现在已经确定“行业标准”洗牌现在是Fisher-Yates随机播放。

答案 10 :(得分:1)

使用/ dev / ramdom(linux device true random number generator)来播种mt_rand

<?
$rnd_dev=mcrypt_create_iv(4, MCRYPT_DEV_RANDOM); //need "apt-get install php5-mcrypt"
$seed=ord(substr($rnd_dev, 0, 1))<<24 |
      ord(substr($rnd_dev, 1, 1))<<16 |
      ord(substr($rnd_dev, 2, 1))<<8 |
      ord(substr($rnd_dev, 3, 1));
mt_srand($seed);
echo mt_rand();
?>

答案 11 :(得分:0)

我创建了一个PHP类来生成随机数和字符串PHPRandomValue

它使用&#34; mcrypt_create_iv(4,MCRYPT_DEV_URANDOM)&#34;生成随机数和值。我在加密项目时做了这个,因为我需要一个安全的随机值生成器。这是一个示例用法

$randomValue = new RandomValue;

$randomValue->randomNumber(): = -3880998

$randomValue->randomNumberBetween(1,10): = 2

$randomValue->randomTextString(): = CfCkKDHRgUULdGWcSqP4

$randomValue->randomTextString(10):  = LorPIxaeEY

$randomValue->randomKey(): = C7al8tX9.gqYLf2ImVt/!$NOY79T5sNCT/6Q.$!.6Gf/Q5zpa3

$randomValue->randomKey(10):  = RDV.dc6Ai/

答案 12 :(得分:-1)

不可能生成真正的随机数,你可以期望的最好的是伪随机,这是rand()提供的,你的函数不再接近随机然后rand()。看一下这个http://en.wikipedia.org/wiki/Random_number_generator

答案 13 :(得分:-1)

Tru随机数

<?php
for ($i = -1; $i <= 4; $i++) {
    $bytes = openssl_random_pseudo_bytes($i, $cstrong);
    $hex   = bin2hex($bytes);

    echo "Lengths: Bytes: $i and Hex: " . strlen($hex) . PHP_EOL;
    var_dump($hex);
    var_dump($cstrong);
    echo PHP_EOL;
}
?>

并加密安全;)

答案 14 :(得分:-2)

虽然几年前答案已被接受,但我会重新开启。

由于所有这些随机性取决于系统时间,让我们也搞乱系统时间!操作在计算机上花费的时间实际上是相当可变的(特别是如果在该服务器上发生其他事情),所以如果我们利用microtime的那个...(找不到任何便携式纳米级命令)

$a='';
for (int $i=0; $i<9001; $i++)
{
    usleep(mt_rand(1000,10000));//Also eliminates timing attacks... possibly?
    $a=hash('SHA512',$a.uniqid(mt_rand().microtime(),true));
}
echo $a;

名义上它有207023位的熵,因为你每次迭代都会增加另外23位,但是它有很多相互依赖性,所以它可能要少几个数量级。还不错。

你知道PHP上的任何操作需要花费很多时间吗?就像...... HTTP请求某个网站(RANDOM.org除外)并测量所需的时间?

答案 15 :(得分:-4)

使用random.org,您可以使用:

function getToken($length, $min, $max){
    $r = explode('
',file_get_contents('http://www.random.org/integers/num='.$length.'&min='.$min.'&max='.$max.'&col=1&base=10&format=plain'));

    $string = '';
    foreach ( $r as $char ) $string.=$char;
    return $string;
}

这应该给出真正的随机数