HMAC-ed 密码是否比 bcrypt-ed 或 scrypt-ed 密码更安全?

信息安全 密码 哈希 bcrypt 加密 hmac
2021-08-23 03:11:20

给定一个选项,我应该选择哪一个,用于安全存储密码的 HMAC 或 bcrypt 或 scrypt 库?

3个回答

为了让您对计算密码哈希的问题和微妙之处有一个正确的认识,以及为什么 HMAC 不适合这个问题,我将提供一个比直接回答这个问题真正必要的更广泛的答案。

HMAC 散列算法本质上只是普通散列算法的键控版本。它通常用于验证完整性和真实性。通常的表示法是H(m,k) = h,其中H是 HMAC 散列算法,m是消息,k是密钥,h是结果散列。这个想法是,共享秘密的两方k可以验证对方是m. 此外,攻击者无法在不知道的情况下伪造消息哈希k

这是按如下方式完成的:

  1. Alice 和 Bob 都知道一个共享的密钥k
  2. Alice 写了一条消息m,并使用kie计算它的 HMAC 散列H(m,k) = h
  3. Alice 将消息m和哈希发送h给 Bob。
  4. Bob 计算H(m,k)并将其与hAlice 发送的哈希值进行比较。如果哈希匹配,他就知道 Alice 发送了消息,并且在她对它进行哈希处理后它没有被更改。

现在您已经了解了 HMAC 是什么,让我们继续讨论您真正想做的事情 - 将密码存储在数据库中。

许多年前,将密码以明文形式存储在数据库中是标准做法。这是一个坏主意,因为当数据库遭到破坏时,攻击者获得了所有密码。为了解决这个问题,我们开始使用单向加密哈希算法对数据库中的密码进行哈希处理。MD5 开始流行,但其中发现的弱点(碰撞、部分原像等)意味着不再推荐它。很多人转向更安全的 SHA1。

这种方法的问题是可以构建一个巨大的哈希表及其对应的明文。这些被称为彩虹表他们的工作理念是,为所有可能的密码(在某个集合内)计算一个巨大的哈希列表并存储它,这样以后可以快速查询它,这样会更有效。因此,无需暴力破解单个散列,而是可以只查询数据库中的散列并立即返回其明文。

为了解决这个问题,安全书呆子发明了盐。盐是在散列之前附加到密码的大型唯一随机值。该盐与散列一起存储,以便以后可以再次计算。因此,我们计算H(m+s) = h,然后将h和存储s在数据库中。这为防止彩虹表提供了重要的保护,因为它本质上需要为每种盐生成一个单独的彩虹表。

所以,坏人转回字典攻击和蛮力破解。随着 GPU 计算的出现,在中等强大的显卡上每秒计算数十亿次哈希成为可能。事实上,人们已经建造了每秒可以计算近500 亿次 MD5 哈希的计算机——这非常令人印象深刻/可怕。GPU 能够做到这一点的原因是它们被设计为执行大量并行标量操作。标量运算是不涉及分支的数学和逻辑运算——即它们不需要做太多/任何“如果 x 则做 y”。密码散列算法往往适合这个模型。

为了使这变得困难,我们必须使散列操作足够慢以使暴力破解不可行。普通的散列算法(例如 SHA1)被设计得很快,这使得它们不适合这个目的。HMAC 增加的开销很小,也没有额外的安全余量,所以在这里也没有多大用处。

创建一个慢速加密散列算法说起来容易做起来难——很难想出一个速度慢、不可约(即无法优化超出其当前状态)和安全的算法。有三种流行的哈希函数可以做到这一点:PBKDF2bcryptscrypt这些被称为自适应密钥派生函数,因为它们接受工作因子值以及明文和盐。工作因子改变了计算哈希所需的时间,旨在防止未来的硬件改进。

因此,对于自适应密钥派生算法H,我们计算H(m,s,w) = h,其中m是消息(密码),s是盐,w是工作因子。结果h通常包含sw,以便验证函数稍后可以使用相同的参数计算相同的哈希值。工作因子通常控制内部密码原语执行的迭代次数。目标是使计算花费足够长的时间以使破解不可行,但不超过我们拥有的资源。

为了针对基于硬件的专用破解提供更高的安全性,scrypt 确保哈希计算是 CPU 硬和内存硬的,即它需要大量的 CPU 和内存资源才能产生哈希值。这很重要,因为FPGA通常只能访问很少的即时存储器。

现在,一个明显的问题出现了:如果我将我的站点设置为使用 bcrypt,这是否意味着我的服务器必须一直计算 CPU 密集型哈希?好吧,如果您在服务器上运行它们,那么是的,这是需要考虑的事情。更好的解决方案是让客户端计算哈希,然后通过 SSL 将其发送到服务器。然后,服务器可以将其与数据库中的值进行比较。这确保了密码散列在被盗时不会被轻易破解(例如通过数据库泄露),并且您的服务器不会被密码散列计算的开销所淹没。对于网站,您可以使用jsBcrypt 更新:下面的评论者指出此方法存在缺陷。不要使用它。

希望这能让您对情况有一个很好的了解,以及为什么 HMAC 不适合这种用途。

HMAC是消息认证码它使用密钥。Bcrypt没有。因此,选择不是中立的;你不能认为一切都是平等的,因为它们不是。

尽管名义上用于完整性检查,但 HMAC(当与相当安全的散列函数一起使用时,例如 SHA-256 甚至 SHA-1)在某种程度上表现得像“带有密钥的散列函数”。这不是 MAC 算法的通用属性,但它适用于 HMAC(这就是为什么它可以用作名为Hmac_DRBG的随机生成器的基础)。这使得 HMAC 成为密码散列的潜在选择。

如果您使用 HMAC “散列”您的密码,并且攻击者可以获取您的散列密码文件/数据库,但不能获取 HMAC 密钥,那么攻击者将无法破解密码。在那种情况下,HMAC 比 bcrypt/scrypt “更好”。然而,这是一个极端情况。我们对密码进行哈希处理是因为我们担心攻击者可能会破坏安全性足以获得对部分服务器文件的只读访问权限,而不是读写访问权限的边缘场景。hash-with-HMAC 方法适用于边缘平方方案,其中只读攻击者可以获得散列密码,但无法获得密钥,但密钥仍存储在同一台服务器上。

如果攻击者可以得到密钥,那么 HMAC 对他来说只是一个简单的哈希函数,在这种情况下,HMAC 比 bcrypt 差得多,因为它是无盐的而且太快了就像密码使用一对 SHA-1 调用进行哈希处理一样。这种情况的可能性不亚于前一种情况,因此我们必须考虑单独使用 HMAC 风险太大


为了两全其美,在用户密码上应用 HMAC,然后通过bcrypt处理 HMAC 输出您将存储 bcrypt 的输出。由于 bcrypt 实现期望密码为字符串,因此您必须对 HMAC 输出(十六进制、Base64 ...)进行编码。

这比单独使用 bcrypt 更复杂,因为使用了两个功能而不是一个,并且因为密钥(这带来了密钥管理的整个棘手问题:生成、存储、备份......)。由于复杂性不好,我建议不要这样做;只需单独使用 bcrypt,不要添加 HMAC。但是,这是您的电话;如果您真的想要HMAC,请bcrypt之外使用它,而不是代替bcrypt。

(注意:这是 HMAC 输出上的 bcrypt,而不是相反,由于 bcrypt 输出中包含盐,这将不起作用。)

HMAC 的设计速度非常快,在这种情况下,它是一种向密码添加盐而不是仅仅附加它的好方法。由于初始化缓慢,Bcrypt 慢得多,而 scrypt 甚至比 Bcrypt 慢,因为它是故意以这种方式设计的。Scrypt 旨在使暴力破解在计算上非常昂贵。它消耗大量 CPU、内存,并且在 GPU 上使用也很慢。

更多关于 scrypt