我要发布答案,因为一些现有答案很接近,但具有以下一项:
- 字符空间比您想要的要小,因此对于相同的熵,暴力破解会更容易,或者密码必须更长
- 不被视为加密安全的RNG
- 某些第三方图书馆的要求,我认为展示自己可能需要做的事情可能很有趣
该答案将解决该count/strlen
问题,因为生成的密码(至少是IMHO)的安全性将超越您的到达方式。我还要假设PHP> 5.3.0。
让我们将问题分解为以下组成部分:
- 使用一些安全的随机性源来获取随机数据
- 使用该数据并将其表示为一些可打印的字符串
在第一部分中,PHP> 5.3.0提供了功能openssl_random_pseudo_bytes
。请注意,尽管大多数系统使用加密强度高的算法,但您必须进行检查,因此我们将使用包装器:
/**
* @param int $length
*/
function strong_random_bytes($length)
{
$strong = false; // Flag for whether a strong algorithm was used
$bytes = openssl_random_pseudo_bytes($length, $strong);
if ( ! $strong)
{
// System did not use a cryptographically strong algorithm
throw new Exception('Strong algorithm not available for PRNG.');
}
return $bytes;
}
对于第二部分,我们将使用base64_encode
它,因为它需要一个字节字符串,并将产生一系列字符,这些字符的字母非常接近原始问题中指定的字母。如果我们不介意使用+
,/
并且=
字符出现在最后的字符串中,并且我们希望结果至少包含$n
字符,那么我们可以简单地使用:
base64_encode(strong_random_bytes(intval(ceil($n * 3 / 4))));
的3/4
因素是由于这样的事实:base64编码在具有长度比字节串的至少三分之一大的字符串结果。结果将精确为$n
4的倍数,否则最多3个字符。由于多余的字符主要是填充字符=
,如果由于某种原因我们限制了密码的长度,则可以将其截短为所需的长度。尤其是因为对于给定的$n
密码,所有密码都将以相同的密码结尾,这样,有权使用结果密码的攻击者最多可以少猜两个字符。
为了获得额外的荣誉,如果我们想满足OP的要求中的确切要求,那么我们将不得不做更多的工作。我将放弃此处的基本转换方法,而采用一种快速而又肮脏的方法。由于有62个输入项的长字母,因此两者都需要产生比将在结果中使用的随机性更多的随机性。
对于结果中的多余字符,我们可以简单地将它们从结果字符串中丢弃。如果我们从字节字符串中的8个字节开始,那么多达“ base64”字符的大约25%将是这些“不需要的”字符,因此简单地丢弃这些字符将导致字符串的长度不小于OP所需的长度。然后,我们可以简单地将其截断以精确到长度:
$dirty_pass = base64_encode(strong_random_bytes(8)));
$pass = substr(str_replace(['/', '+', '='], ['', '', ''], $dirty_pass, 0, 8);
如果生成的密码较长,则填充字符=
在中间结果中所占的比例会越来越小,因此,如果要耗尽PRNG所使用的熵池,则可以实现更精简的方法。