
The hmac-bcrypt password hashing function
此仓库包含 hmac-bcrypt 密码哈希函数在多种语言中的参考实现。每份参考实现都尽可能尝试对 @epixoip 创建的原始 C 和 Perl 实现进行 1:1 移植,并且各实现之间完全兼容(即,它们生成并校验相同的哈希值)。
每份参考实现都定义了以下两个过程式函数,其伪原型如下:
string hmac_bcrypt_hash(password, settings?, pepper?)
boolean hmac_bcrypt_verify(password, expected, pepper?)
请参阅每份参考实现附带的测试用例,了解如何在您的项目中集成和使用这些函数。选择过程式接口是为了简单起见,但您可以根据需要自由地将这些函数封装到类或对象中。
在此上下文中,settings 参数指标准的 bcrypt 设置字符串,包含哈希标识符(2a)、log2 成本(例如 13),以及可选的 22 字节、radix64 编码的盐值(例如 LhayLxezLhK1LhWvKxCyLO)。这些值以美元符号分隔拼接成一个字符串;例如,$2a$13$LhayLxezLhK1LhWvKxCyLO。
settings 参数是可选的;大多数情况下,应将其留空/null,以使用默认成本 13 和自动生成的盐值。最多,如果您希望使用 13 之外的成本值,可以只提供 id + 成本值(例如,$2a$10$)。不建议自行创建并提供盐值!
pepper 参数定义了一个全局共享密钥,同样也是可选的;如果它为 null/空白,则使用默认值 hmac_bcrypt。这主要用于抵御 shucking 攻击,但也可用于提高破解的安全性、难度和成本(尤其是在与 HSM 结合使用时)。
hmac-bcrypt 密码哈希函数采用 bcrypt,并进行适当的前哈希和后哈希处理,同时结合可选的 pepper。用伪代码表示,这相当直接:
pre_hash = hmac_sha512_base64(password, pepper)
mid_hash = bcrypt(pre_hash, settings)
post_hash = hmac_sha512_base64(mid_hash, pepper)
return settings + post_hash
采用前哈希是为了支持超过 bcrypt 最大 72 输入字节的输入长度。选择 SHA-512 是因为其 64 位字长,这对 CPU 防御者友好,但会阻碍 GPU 攻击者。然而,原始的 SHA-512 值不能直接使用,原因如下:
为了缓解 shucking 攻击,前哈希必须加盐——或者在这种情况下,加 pepper——而 HMAC 为哈希提供密钥提供了一种便捷的方式。随后,HMAC 值以 base64 编码,以生成干净的低位 ASCII 输入,从而缓解 null 字节和二进制数据带来的问题。
细心的读者会注意到,hmac_sha512_base64 产生 88 字节的数据,而 bcrypt 的最大输入大小为 72 字节。这不是问题,实际上反而优于使用 sha256 等产生较少输入数据的哈希算法。我们希望填满全部 72 个字节,并且将 sha512 截断到 432 位不会损失安全性(这大于 sha384 提供的 384 位)。
采用后哈希主要是为了区分 hmac-bcrypt 哈希与 bcrypt 哈希——即,长度会不同——同时也为 pepper 增加一层额外的保护。甚至可以结合存储在 HSM 中的 pepper 值来执行后哈希步骤(强烈推荐!)以获得进一步保护。
虽然内存硬度一直是一个有趣的实验,但实现抗加速的正确路径显然是缓存硬度。内存速度和带宽持续提升,而 RAM 变得更大、更便宜、更密集。但缓存大小、缓存速度和缓存成本相对稳定。即使是硬件 scatter/gather 指令,也未对缓存硬算法产生我们曾预测的那种巨大影响。
对于目标运行时间小于 1000ms 的情况,最佳的内存硬算法——Argon2 和 scrypt——实际上比缓存硬算法的抗加速能力更差,这使它们成为出色的 KDF,但不太适合实时身份验证。
理想情况下,人们会使用故意采用缓存硬设计的密码哈希函数,例如 pufferfish 或 bscrypt。然而,这些函数较新、研究较少,且可用的库很少。而 bcrypt——尽管是无意中成为缓存硬——几乎可以在每种语言和框架中直接使用。在我们可用的算法中,bcrypt 为实时、交互式身份验证(目标运行时间 < 1000ms)提供了最强的抗加速能力,因此显而易见的答案就是利用我们手头可用的 bcrypt。
然而,bcrypt 确实有一些明显的局限性,正如其直言不讳的批评者迅速指出的那样:
hmac-bcrypt 解决了这两个问题,甚至更多。