【问题标题】:str_shuffle and randomnessstr_shuffle 和随机性
【发布时间】:2012-12-14 07:41:33
【问题描述】:

不久前,我编写了一个随机字符串生成器,它使用字符串中的第 mt_rand() 个字符构建字符串,直到达到所需的长度。

public function getPassword ()
{
    if ($this -> password == '')
    {
        $pw             = '';
        $charListEnd    = strlen (static::CHARLIST) - 1;
        for ($loops = mt_rand ($this -> min, $this -> max); $loops > 0; $loops--)
        {
            $pw .= substr (static::CHARLIST, mt_rand (0, $charListEnd), 1);
        }
        $this -> password   = $pw;
    }
    return $this -> password;
}

(CHARLIST 是一个包含密码字符池的类常量。$min 和 $max 是长度约束)

今天,在完全研究其他东西时,我偶然发现了以下代码:

function generateRandomString ($length = 10) {    
    return substr(str_shuffle ("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, $length);
}

这与我在一行中循环基于 mt_rand() 的代码的效果几乎相同。我真的很喜欢它,原因很简单,更少的代码行总是一件好事。 :)

但是当我在 PHP 的手册中查找 str_shuffle 时,它​​的文档非常简单。我真正热衷于学习的一件事是它使用什么算法来实现随机性?该手册没有提到要进行什么样的随机化来获得打乱的字符串。如果它使用 rand() 而不是 mt_rand() 那么坚持我当前的解决方案可能会更好。

所以基本上我想知道 str_shuffle 如何随机化字符串。是使用 rand() 还是 mt_rand()?我正在使用我的随机字符串函数来生成密码,所以随机性的质量很重要。

更新:正如已经指出的那样,str_shuffle 方法不等同于我已经在使用的代码,并且由于字符串的字符与输入保持相同,因此随机性会降低,仅随着他们的订单改变。但是,我仍然对 str_shuffle 函数如何随机化其输入字符串感到好奇。

【问题讨论】:

  • 这两个功能不一样。使用str_shuffle()的版本不会有任何重复,因此即使RNG相同,随机性也较小。
  • 你是对的,刚刚意识到这一点。
  • @Barmar 换句话说 RTFS?这充其量只是一个陈词滥调的答案。
  • 这就是为什么我把它放在评论中,而不是答案。
  • str_shuffle() 在内部使用 rand()。这是一个弱 PRNG,它是可预测的,并且它只有 2^32 个可能的输出。

标签: php string random passwords


【解决方案1】:

更好的解决方案是mt_rand,它使用Mersenne Twister,效果更好。

正如已经指出的那样,str_shuffle 方法并不等同于我已经在使用的代码,并且由于字符串的字符与输入保持相同,只是它们的顺序发生了变化,因此随机性会降低。但是我仍然对 str_shuffle 函数如何随机化其输入字符串感到好奇。

要使输出相等,我们只需使用0,1 并查看每个函数的可视化表示

简单的测试代码

header("Content-type: image/png");
$im = imagecreatetruecolor(512, 512) or die("Cannot Initialize new GD image stream");
$white = imagecolorallocate($im, 255, 255, 255);
for($y = 0; $y < 512; $y ++) {
    for($x = 0; $x < 512; $x ++) {
        if (testMTRand()) { //change each function here 
            imagesetpixel($im, $x, $y, $white);
        }
    }
}
imagepng($im);
imagedestroy($im);

function testMTRand() {
    return mt_rand(0, 1);
}

function testRand() {
    return rand(0, 1);
}

function testShuffle() {
    return substr(str_shuffle("01"), 0, 1);
}

输出 testRand()

输出 testShuffle()

输出 testMTRand()

所以基本上我想知道 str_shuffle 如何随机化字符串。是使用 rand() 还是 mt_rand()?我正在使用我的随机字符串函数来生成密码,所以随机性的质量很重要。

您可以清楚地看到str_shuffle 产生与rand 几乎相同的输出...

【讨论】:

  • 令人讨厌的迂腐吹毛求疵:不同的算法可以有相同的输出。也有可能它们仅在范围为 [0,1] 时表现相同。 非常不太可能。无论哪种方式,+1。我喜欢漂亮的图片:)。
  • testShuffle 不会产生与testRand 几乎相同的输出,它会产生完全相反的结果(在您的测试中):-)
  • 我是根据你的输出来判断的。如果从 testRand() 反转输出,它与 testShuffle() 完全相同。
  • 你确定吗?我认为这两张图片是 100% 相互对立的,Paint.net 似乎同意我的看法。
  • 快速更新:starting from PHP 7.1rand()mt_rand()的别名,所以上面三个测试方法会一样
【解决方案2】:

请注意,如果您的应用程序真正关注安全性,则不应使用此方法。 Mersenne Twister 不是加密安全的。 PRNG 可以产生在统计上看起来是随机的但仍然很容易破坏的值。

【讨论】:

    【解决方案3】:

    仍然不是加密安全的,但这里有一种使用 str_shuffle() 的方法,同时允许字符重复,从而提高复杂性...

    generate_password($length = 8, $strength = 3) {
        if ($length < 6) $length = 6;
        if ($length > 32) $length = 32;
        // Excludes [0,O,o,1,I,i,L,l,1] on purpose for readability
        $chars = 'abcdefghjkmnpqrstuvwxyz';
        if ($strength >= 2) $chars .= '23456789';
        if ($strength >= 3) $chars .= strtoupper($lower);
        if ($strength >= 4) $chars .= '!@#$%&?';
        return substr(str_shuffle(str_repeat($chars, $length)), 0, $length);
    }
    

    $chars 重复 $length 次,然后再对字符串进行混洗,这比仅混洗一次要好一些。

    我们只在不存储敏感信息的系统中使用它;)

    【讨论】:

    • 一项改进可能是逐个构建字符串,检查最后一个字符以确保两个相同的字符不按顺序排列,但您明白了 ;)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-09-19
    • 1970-01-01
    • 2016-04-06
    • 1970-01-01
    • 1970-01-01
    • 2016-05-16
    • 1970-01-01
    相关资源
    最近更新 更多