加密安全的随机字符串函数

5
目标:寻找最安全的随机字符串生成器。在字符串中使用字母、数字,如果可能的话还要加上特殊字符。
我已经在这里和其他地方阅读了很多文章,但仍然听到很多不同的答案/意见。希望了解安全和密码学的人可以在这里发表意见。
以下函数将用于生成一个8个字符的随机密码和一个128个字符的随机令牌。
函数1:
/**
 * Used for generating a random string.
 *
 * @param int $_Length  The lengtyh of the random string.
 * @return string The random string.
 */
function gfRandomString($_Length) {
    $alphabet = "abcdefghijklmnopqrstuwxyzABCDEFGHIJKLMNOPQRSTUWXYZ0123456789";
    $pass = array(); //remember to declare $pass as an array
    $alphaLength = strlen($alphabet) - 1; //put the length -1 in cache
    for ($i = 0; $i < $_Length; $i++) {
        $n = rand(0, $alphaLength);
        $pass[] = $alphabet[$n];
    }
    return implode($pass); //turn the array into a string
}

功能2:

PHP.net文档中提到: crypto_strong: 如果传入函数,它将持有一个布尔值,用于确定算法是否是“密码学安全的”,例如,对于GPG、密码等使用来说是安全的。如果是,则为TRUE,否则为FALSE。

那是基于服务器吗?如果我测试一次,并且它能够生成一个crypto_strong字符串,它会一直保持这样吗?还是每次都需要检查并创建循环直到生成一个crypto_strong字符串。

/**
 * Used for generating a random string.
 *
 * @param int $_Length  The length of bits.
 * @return string The random string.
 */
function gfSecureString($_Length) {
    $Str = bin2hex(openssl_random_pseudo_bytes($_Length));
    return $Str; 
}

我欢迎任何改进加密强度的建议。


哈哈,那是我看到的主要问题之一。这个问题引起了很多人的争论,而被接受的答案使用了rand()函数,这让很多人不同意。这个问题的重点是实际上看看哪一个更安全。真正的答案,而不仅仅是想法或观点。 - M H
1
你可能需要稍微阅读一下它们(提示:random_int)。或者等待赏金标记。✔勾号在那里最不有用的答案上并不会使它变得不是重复的。让我们不要每周都用新问题来稀释主题。 - mario
1
那个问题与加密安全毫无关系。因此,被接受的答案并不安全。我的问题完全朝着不同的方向发展。 - M H
我正在寻找一个加密安全的字符串生成函数,以我的当前函数为起点。你标记为重复的问题只是在询问一个随机生成器函数。这就是为什么他将不安全的函数标记为答案,因为它完成了他所要求的任务。现在,那个问题已经得到解答,而我的问题却被关闭了,我将无法得到有关生成加密安全字符串函数的真正答案。 - M H
1
请查看此答案:https://dev59.com/5W025IYBdhLWcg3wST2Q#31284266。 - Scott Arciszewski
显示剩余5条评论
2个回答

8
所以您想在PHP中安全生成随机字符串。问题中的这两个函数都不能给您想要的结果,但rand()解决方案是其中最糟糕的。rand()不安全, 而bin2hex(openssl_random_pseudo_bytes())会限制您的输出字符集。
此外,openssl_random_pseudo_bytes()在极端条件或奇特设置下可能不可靠
据我所知,只有当RAND_pseudo_bytes()未返回任何数据时,crypto_strong才会被设置为false。如果调用OpenSSL时未进行种子处理,则它将悄无声息地返回弱(可能是可预测的)伪随机字节。从PHP的角度来看,您无法确定它是否是随机的。

如何生成安全的随机字符串

如果你想要一个在PHP 5.x中已经接受了大量评审的解决方案,请使用RandomLib

$factory = new RandomLib\Factory;
$generator = $factory->getMediumStrengthGenerator();
$randomPassword = $generator->generateString(20, $alphabet);

替代方案

如果您不想使用RandomLib(即使只是因为您想拥有备选选项),您也可以在PHP 7发布时使用random_int()。如果您等不及那时,请查看我们的random_compat项目。

如果您正在使用密码库libsodium,则可以这样生成随机数:

/**
 * Depends on the PECL extension libsodium
 * 
 * @link https://dev59.com/OY3da4cB1Zd3GeqPvyev#31498051
 * 
 * @param int $length How long should the string be?
 * @param string $alphabet Contains all of the allowed characters
 * 
 * @return string
 */
function sodium_random_str($length, $alphabet = 'abcdefghijklmnopqrstuvwxyz')
{
    $buf = '';
    $alphabetSize = strlen($alphabet);
    for ($i = 0; $i < $length; ++$i) {
        $buf .= $alphabet[\Sodium\randombytes_uniform($alphabetSize)];
    }
    return $buf;
}

请查看this answer,以获取使用random_int()的示例代码。我不想在未来需要更新代码时重复努力。


2

openssl_random_pseudo_bytes的加密安全性较高,而rand则不是。然而,它只返回二进制数据,你需要将其还原为十六进制数。十六进制数不足以生成密码字符串。两种函数均不包含你所需的特殊字符。

因此,这两个代码片段都不能满足你的目的。


是的,但是将它从bin2hex更改为生成密码并不容易。首先,您需要将其更改为在范围内创建一个值(而不会失去随机性),然后您必须使用该值安全地生成密码。 - Maarten Bodewes
Maarten:你对random_compat有什么想法? :) - Scott Arciszewski
@ScottArciszewski 看起来这是一个写得很好的API(实际上是函数调用),它按优先顺序从多个来源检索随机字节。目前Source Forge已经离线,但我认为奇怪的是,一个未维护的库(带有生成IV的功能)被认为是主要来源。Codes已经看过它了,所以我肯定会很信任它,但对我来说感觉太像临时措施了。不过,用PHP编写的PRNG可能会太慢,并且可能存在其他安全问题。本身而言,它并不能解决这个问题(没有范围数字)。 - Maarten Bodewes
Sourceforge?我不确定你在看什么。 - Scott Arciszewski
哦,我刚刚重新阅读了你的评论,现在知道你的意思了。这个链接或许可以帮到你 :) https://github.com/paragonie/random_compat/blob/master/ERRATA.md - Scott Arciszewski
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接