apache中的mod_php何时为随机数生成器种子


When does mod_php in apache seed the random number generator?

我正在使用php rand()函数为我的电子商务系统生成优惠券代码。它运行了一段时间,但现在我收到了很多错误,因为代码已经在系统中了。

这是我使用的功能:

function generateRandomString($length) {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyz';
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, strlen($characters) - 1)];
    }
    return $randomString;
}   

我的代码有32个字符长。

我做了大约150次尝试的样本,注意到超过50%的生成代码在系统中已经存在。

我在系统中有4212个代码。一个32个字符的随机字符串与36个不同的符号发生碰撞的几率基本为零,我得到了50%的碰撞。

当我通过调用srand();在函数中重新设置随机数生成器的种子时,我不再有任何冲突。

但在php的手册页上,它清楚地说:

注意:从PHP 4.2.0开始,不需要对随机数进行种子带有srand()或mt_srand()的生成器自动地

我正在运行php版本PHP 5.5.9

因此,我的想法是,像这样的种子接种已经完成,但每个Web服务器工作人员只进行一次,然后当进程被分叉时,它就不会被重新播种或类似的事情。但这显然是apache中的一个错误。。。

我在apache版本Apache/2.4.7 (Ubuntu)mpm_prefork_module模块中以apache模块的身份运行php

那么,我是否仍然需要在每个脚本的顶部调用srand()来显示手册页中的其他内容,为什么?是apaches的错还是PHP的错?

是的,我知道我不应该为此目的使用这个函数,我会更新它以使用加密安全数字。但我认为这无论如何都不应该发生,我仍然对正在发生的事情感兴趣!

如果您的代码有32个字符长,那么为什么不简单地用md5加密当前的微时间呢?

  $coupon = md5( microtime() );

一行字很简单。如果你想要一点随机性,就扔一个

   $coupon = md5( microtime() . mt_rand( 0, 10000) );

就像盐一样。这几乎可以保证你永远不会重复。至于为什么它不是随机的。

PHP的随机数生成器在每个进程中只播种一次。

查看此帖子。。。

http://phpsecurity.readthedocs.org/en/latest/Insufficient-Entropy-For-Random-Values.html

顺便说一句,我认为你不需要加密安全的哈希,只需要那些不容易猜测的足够随机的哈希。即使使用加密哈希,用户也会将所述哈希输入优惠券的购物车中,即使是加密安全的哈希,也很容易使用暴力。因此,您最好将时间投入到每秒只允许"n"次尝试或"n"个尝试等。为了降低暴力攻击的发生率。

例如,我会尝试32个字符的散列的所有组合。所以最终这并不重要,因为你没有使用像密码这样的明文条目,然后隐藏你的salting和加密方法。激活优惠券的数量将决定我的成功率和在任何一种情况下所需的时间。。。如果你跟随。

我的答案中隐含着这个

PHP的随机数生成器在每个进程中只播种一次。分叉不会创建新流程,而是复制当前流程状态。

请参阅
在分叉的子级上调用rand/mt_rand会产生相同的结果

http://wiki.openssl.org/index.php/Random_fork-safety

http://www.reddit.com/r/shittyprogramming/comments/2jvzgq/sometimes_it_takes_real_shitty_code_to_expose_an/

此外,这不是php特有的问题,而是一般伪随机数生成的问题。

请参阅:https://github.com/php/php-src/blob/d0cb715373c3fbe9dc095378ec5ed8c71f799f67/ext/standard/rand.c#L66-L68

显然,RNG在第一次调用到rand()(或显式调用srand())时被重新播种。

由于fork复制了父母的内存,所以孩子也会获得父母的种子——永远不会被重新播种。

function generateRandomString($length) {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyz';
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, strlen($characters) - 1)];
    }
    return substr(time().$randomString,0,$length);
}