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

caw*_*caw 17 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];
}
Run Code Online (Sandbox Code Playgroud)

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

coo*_*ird 33

伪随机数发生器(PRNG)是非常复杂的野兽.

有没有真正的"完美"的随机数生成器 - 事实上,可以从数学函数来完成最好的是伪随机 - 他们似乎足够的随机对大多数的意图和目的.

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

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

坦率地说,看起来这个mt_rand功能使用的是Mersenne twister,它是一个相当不错的PRNG,因此对于大多数休闲用途来说它可能已经足够好了.

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

编辑

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

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

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

  • karim:由于所有PRNG都是基于刚刚产生明显随机数的计算,因此存在某些缺点.原则上,生成"随机"数字与计算机可以做的事情相悖,因为计算机是设计确定性的机器.根据对数字执行哪些计算以生成随机位,有一些表现出统计上良好的随机性,而另一些则没有.这取决于你的发电机.LCG具有弱的低位,其他类型具有弱的高位,其他类型甚至没有表现出任何弱点.这是一个非常复杂的话题. (4认同)
  • 马克:如果有疑问,就不要.插入伪随机数需要非常小心,不要破坏结果.如果你对自己所做的事情一无所知,那么你很可能会让事情变得更糟. (2认同)

Sal*_*ali 25

快速回答:

在新的PHP7中,最终支持加密安全的伪随机整数.

int random_int ( int $min , int $max )
Run Code Online (Sandbox Code Playgroud)

PHP5x还有一个polyfill.

更长的答案


没有完美的随机数生成器,计算机使用伪随机数生成器来创建看起来随机的序列.序列看起来是随机的(并通过一些随机性测试),但由于有一些算法可以生成它,你可以用完全相同的状态重复算法并得到相同的结果.

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


随机数生成器的子集之一是加密安全随机数生成器:

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

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


但幸运的是PHP7实现了它,

int random_int ( int $min , int $max )
Run Code Online (Sandbox Code Playgroud)

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

随机来源如下:

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

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


Pet*_*ter 17

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

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


tru*_*ppo 13

以什么方式mt_rand()"坏"?

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

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


man*_*oid 12

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

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

受欢迎的需求:D

  • 不确定那些不读xkcd的人会得到这个笑话.可能会更好地链接它. (4认同)

小智 5

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

  • 这引入了在刷新之前在阵列中重复使用相同数字的可能性,即使千位数来自更大范围,这是可能值的整数范围小于整数范围的32位整数.更好的方法是将数组视为队列; 根据需要添加新值,始终使用第一个项目并在之后丢弃它. (2认同)