关于php:str_shuffle和随机性

str_shuffle and randomness

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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是长度约束)

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

1
2
3
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函数如何随机化其输入字符串感到好奇。


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

As has been pointed out, the str_shuffle method is not equivalent to the code I'm already using and will be less random due to the string's characters remaining the same as the input, only with their order changed. However I'm still curious as to how the str_shuffle function randomizes its input string.

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

简单测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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()

enter


请注意,如果您的应用程序确实专注于安全性,则不应使用此方法。 Mersenne Twister并非加密安全。 PRNG可以产生统计上似乎是随机的但仍然容易破坏的值。


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

1
2
3
4
5
6
7
8
9
10
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次,这比仅对单个匹配项进行混洗要好一些。

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