对于散列 128 位随机数 API 密钥,单次迭代 SHA-256 是否足够安全?

信息安全 密码学 密钥管理 蛮力 bcrypt sha256
2021-08-24 13:43:54

我为我的 REST API 实现了这种身份验证,这安全吗?我的逻辑是否合理?

我主要对SHA-256 哈希和迭代 = 1方面感到好奇。我省略了一些关于其他方面的细节和解释,以使这个问题更简单、更集中。 

  • 身份验证基于 API 密钥
  • API 密钥是使用 Java UUID.randomUUID() 生成的 128 位随机数。例如 9885e3dd-b584-45d1-a0f0-3295b0ea4324
  • API 密钥本身不会被持久化。API 密钥哈希存储在数据库中
  • 我存储密钥的 SHA-256 哈希。我做了一次迭代:
private static final int ITERATIONS = 1;
private static final String ALGORITHM = "SHA-256";

private byte[] digest(byte[] value) {
    MessageDigest messageDigest = createDigest(ALGORITHM);
    for (int i = 0; i < ITERATIONS; i++) {
        value = messageDigest.digest(value);
    }
    return value;
}

不涉及盐渍。最佳实践建议使用 salting - 和 bcrypt - 来存储密码哈希。据我了解,这是为了使彩虹攻击和字典攻击不可行,并减缓暴力攻击。我决定使用 SHA-256,因为它更快,而且我找不到在我们的用例中使用较慢 bcrypt 的真正理由。 

由于我不是对密码进行哈希处理,而是对 128 位随机数进行哈希处理,因此彩虹攻击和字典攻击不相关。正确的?只有暴力破解。

我目前的理解是暴力破解 128 位密钥是不可能的。我不是核心加密专家,我是基于这样的帖子(实际上将散列减少到单个逻辑门的翻转来进行计算)  https://crypto.stackexchange.com/a/1148因此有无需为等式增加额外的缓慢性,它已经“足够慢”了。

有了这个,我得出结论,使用 bcrypt 的任何原因都不适用于我的特定用例。

这些推论是否正确,并且使用 SHA-256 的单次迭代作为 api 密钥散列来保持密钥散列安全(足够)?

2个回答

您说 API 密钥的大小和随机性否定了密码散列函数的使用是正确的。CSPRNG 生成的 128 位应该可以用单个 SHA-256 散列存储。

API 密钥的最大问题与密码不同,因为它们不能跨站点重复使用,也不能轻易被暴力破解。通常最大的问题是从客户端应用程序本身(或协议)泄露密钥。

这里有很多需要注意的事情,只有第二点与密钥大小有关:

  1. 在生成时仅显示一次密钥(很简单,您已经决定不以明文形式存储它)
  2. 有办法让用户识别 API 密钥这可以通过显示第一个(或最后一个)X 个字符来实现。这意味着您需要存储这些 X 字符,并且可能希望将这些可见位视为攻击者“已知”
  3. 具有各种范围(权限),您可以将其附加到 API 密钥,以限制一个密钥泄露造成的损害。不鼓励(或禁止)使用“做所有事情”范围
  4. 使用相同密钥的速率限制请求并尝试通过 IP 地址限制错误密钥使用的速率(这很棘手,但这是一个相当容易采取的步骤)
  5. 通过https,API 密钥的传输很棘手,有很多选项,请谨慎选择(当然需要 TLS)

PS:话虽如此,如果您担心长度,您可以轻松使用 256 位密钥和比十六进制更紧凑的编码。例如:base64 中的 256 位是 43 个字符。

TLDR:真实密码通常具有低熵。为了防止这种密码的暴力破解,散列算法应该是资源昂贵的。

我觉得你的问题很好。许多开发人员在谈论密码时会说“当然,使用 Argon2 或类似的昂贵哈希”。但他们中没有多少人认为为什么这样做是有意义的。答案并不明显。

如果您的所有用户都拥有 128 位或更多位熵的密码,那么是的,次 SHA-256 迭代就足够了。要获得这样的熵,您可能需要例如使用随机数生成器。此类密码的人类可读表示(例如 base 64)将包含 20 多个字符。您有多少用户真正使用过这样的密码?

在现实中,许多用户使用的密码基于他们的语言单词,可能是 1 个数字和 1 个特殊字符。通常这样的密码只有 50-60 位的熵。快速散列使此类密码的暴力破解成为现实。

为了使暴力破解更加缓慢和昂贵,哈希算法应该消耗更多的 CPU 和 RAM 资源。我不会在这里更详细地解释它,因为我知道你很清楚。