如何使用PHP生成强大的独特API密钥?

Rav*_* CS 3 php uuid

我需要生成一个强大的唯一API密钥。

谁能为此建议最好的解决方案?我不想使用rand()函数来生成随机字符。有替代解决方案吗?

Mic*_*ley 5

从PHP 7.0开始,您可以使用该random_bytes($length)方法生成加密安全的随机字符串。该字符串将采用二进制格式,因此您需要以某种方式对其进行编码。一种简单的方法是使用bin2hex($binaryString)。这将为您提供一个字符串$length * 2字节长,并带有$length * 8一些熵。

您将需要$length足够高,以使您的密钥实际上是不可猜测的,并且生成另一个具有相同值的密钥的可能性几乎为零。

综合所有这些,您将获得:

$key = bin2hex(random_bytes(32)); // 64 characters long
Run Code Online (Sandbox Code Playgroud)

验证API密钥时,仅使用前32个字符从数据库中选择记录,然后用于hash_equals()将用户提供的API密钥与您存储的值进行比较。这有助于防止定时攻击。ParagonIE 在此方面有出色的文章

有关检查逻辑的示例:

$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;
Run Code Online (Sandbox Code Playgroud)

奖励:Base64编码的使用略为先进

出于空间原因,使用Base64编码比使用十六进制更可取,但是由于每个字符编码6位(而不是十六进制为4位),因此使用Base64编码要稍微复杂一些,这可以使编码后的值最后带有填充。

为了避免拖延这个答案,我将提出一些关于在没有其支持参数的情况下处理Base64的建议。选择一个$length大于32且可被3和2整除的数字。我喜欢42,所以我们将其用于$length。Base64编码的长度是4 * ceil($length / 3),因此我们$key将为56个字符长。您可以使用前28个字符从存储中进行选择,最后保留另外28个字符,这些字符可以通过定时攻击来防止泄漏hash_equals

奖励2:安全密钥存储

理想情况下,您应该像对待密码一样对待密钥。这意味着hash_equals您应该像散列密码那样对密钥的其余部分进行散列,而不是用于比较完整字符串,将其与密钥的前半部分(以纯文本形式)分开存储,并使用前半部分进行选择您的数据库,并使用验证后半部分password_verify


Rav*_* CS -9

这是我找到的最好的解决方案。