我需要生成一个强大的唯一API密钥。
有人能为此建议最佳解决方案吗?我不想使用rand()
函数来生成随机字符。有替代解决方案吗?
答案 0 :(得分:5)
从PHP 7.0开始,您可以使用random_bytes($length)
方法生成加密安全的随机字符串。这个字符串将是二进制的,所以你想要以某种方式编码它。一种直截了当的方法是使用bin2hex($binaryString)
。这将为您提供一个长$length * 2
个字节的字符串,其中包含$length * 8
个熵。
您希望$length
足够高,以使您的密钥有效无法使用,并且使用相同值生成另一个密钥的可能性几乎为零。
把这一切放在一起,你就明白了:
$key = bin2hex(random_bytes(32)); // 64 characters long
验证API密钥时,仅使用前32个字符从数据库中选择记录,然后使用hash_equals()
将用户指定的API密钥与存储的值进行比较。这有助于防止定时攻击。 ParagonIE对此有https://stackoverflow.com/users/2830850/tarun-lalwani。
有关检查逻辑的示例:
$token = $request->bearerToken();
// Retrieve however works best for your situation,
// but it's critical that only the first 32 characters are used here.
$users = app('db')->table('users')->where('api_key', 'LIKE', substr($token, 0, 32) . '%')->get();
// $users should only have one record in it,
// but there is an extremely low chance that
// another record will share a prefix with it.
foreach ($users as $user) {
// Performs a constant-time comparison of strings,
// so you don't leak information about the token.
if (hash_equals($user->api_token, $token)) {
return $user;
}
}
return null;
出于空间原因,使用Base64编码优于十六进制,但稍微复杂一点,因为每个字符编码6位(而不是16位十六进制),这可以使编码值在末尾留下填充。
为了保持这个答案不被拖延,我只是提出一些处理Base64的建议而没有他们的支持论点。选择一个大于32的$length
可以被3和2整除。我喜欢42,所以我们将$length
使用它。 Base64编码的长度为4 * ceil($length / 3)
,因此我们的$key
长度为56个字符。您可以使用前28个字符从存储中进行选择,最后留下另外28个字符,以防止因hash_equals
的时间攻击而泄露。
理想情况下,您应该像密码一样对待密钥。这意味着不是使用hash_equals
来比较完整字符串,而是应该像密码一样对密钥的其余部分进行哈希处理,而不是以密钥的前半部分(以纯文本格式)存储,使用上半部分从您的数据库中进行选择,并使用password_verify
验证后半部分。
答案 1 :(得分:1)
您可以尝试使用uniqid
功能。
uniqid - 生成唯一ID
string uniqid ([ string $prefix = "" [, bool $more_entropy = false ]] )
答案 2 :(得分:-2)
使用mcrypt:
<?php
$bytes = mcrypt_create_iv(4, MCRYPT_DEV_URANDOM);
$unpack = unpack("Nint", $bytes);
$id = $unpack['int'] & 0x7FFFFFFF;
答案 3 :(得分:-3)
PHP具有带有可选前缀的uniqid函数http://php.net/manual/en/function.uniqid.php,您甚至可以添加额外的熵以进一步避免冲突。但是,如果你绝对需要一些独特的东西,你就不应该使用任何随机性的东西。
答案 4 :(得分:-8)
这是我找到的最佳解决方案。