BCrypt 的 72 个字符限制并将其用作通用摘要算法

信息安全 验证 哈希 bcrypt
2021-09-01 11:46:51

目标:拥有基于令牌/cookie 的身份验证,不需要在服务器上保持会话

TL;DR:如果有的话,解决 BCrypt 的 72 个字符限制的公认机制是什么?

长版:

阅读此答案后,我尝试使用 BCrypt 作为散列算法和旋转服务器端机密在基于 REST 的服务器上实现身份验证。基本思想是,在使用用户名和密码成功进行身份验证后,服务器将设置一个包含基于以下连接的哈希的 cookie:

  • 服务器端的秘密
  • 用户ID和用户所属的组
  • 发出令牌的时间

然后将使用盐对其进行散列,结果将进入 cookie,由服务器读取和验证(可能是因为用户 ID、组和日期由客户端以明文形式传输,而服务器端秘密在内存中,秘密用作保证客户端没有自己制作所有其他值)。

这一切都很好,直到我意识到 BCrypt 对被散列的明文有 72 个字符的限制(这 72 个字符之后的任何内容都不会影响输出散列)。

这是一个问题,因为上述数据超过 72 个字符,并且允许时间、用户 ID、组或密钥在输入中是“最后一个”而不影响输出,这会在系统的安全性中打开漏洞(无论是旧的令牌是无限有效的,或者人们可以任意修改他们的用户 ID/角色)。

具有讽刺意味的是,当我最终弄清楚为什么我的系统会根据不同的输入生成相同的哈希值并四处搜索时,似乎有些人预见到了这种情况。

无论如何,问题是:如果有的话,解决 BCrypt 的 72 个字符限制的公认机制是什么?

一些先发制人的想法:只要我记得,我就一直在警告我,自己对标准加密算法进行变体,所以我不能 100% 确定什么是最好的行动方案。

明显的选项包括:

  • 分块输入并依次和/或以级联方式将 BCrypt 应用于每个输入;
  • 在输入上使用另一个(非加密?)散列来减小其大小
  • 使用纯压缩(但对于这么少的纯文本可能无效)
  • 切换到另一种算法(例如,最近完成了 SHA-3)。

(上述想法可能还有其他问题,在这种情况下,我当然很乐意得到纠正!)

3个回答

公认的机制是“不要这样做”。

bcrypt 擅长什么?它擅长缓慢为什么你想要一个加密函数,或者只是任何函数,变慢?只有当函数的输入是低熵秘密时,这才有意义,这意味着“对手可以想象和现实地穷尽探索的某个值”。密码是低熵的秘密:众所周知,当人类被允许在他们的大脑隐私中选择秘密字符串时,大多数人会选择对字典攻击完全弱的“机智”密码. 唯一剩下的防御措施是强制使用固有的慢散列过程,这相当于将所有涉及的计算机(尤其是攻击者的计算机)发送回 1980 年代初期,当时 1 MHz 是一个非常高的 CPU 频率。

在您的情况下,“秘密值”是服务器的秘密;它不应该是低熵的(服务器是机器,而不是人;它可以记住长键)。如果服务器的秘密是一个低熵值,那么你做错了。

因此,这里不需要 bcrypt 或任何其他故意减慢的功能。你真正想要的是一个消息验证码:你想在客户端存储一些值(作为 cookie),但你也想检查你没有反馈伪造的值;这是一个完整性问题,而 MAC 是正确的工具。使用 HMAC/SHA-256,将“服务器的秘密”作为 MAC 密钥(您自制的几个值的散列,包括服务器秘密,只是伪装的自制 MAC,因此您可以通过重用标准来正确地做到这一点,强大的、经过专家审查的解决方案,这就是 HMAC)。


当然,一旦我们开始使用客户端存储,存储客户端自己不应该学习的值就会变得很诱人。对于完整性要求,我们添加了机密性为此,我们需要加密。正确地进行加密是很困难的,将加密和 MAC 结合起来更加困难。在这种情况下,请帮自己一个忙:使用经过身份验证的加密模式,它会以最少的麻烦正确完成(我推荐EAX)。


通过建议 Keccak (SHA-3),您证明您患有一种非常常见的疾病,可以称为“iPhone 综合症”:您渴望最新的小工具。停下来。在处理一般安全和特别是密码学时,这只会导致巨大的痛苦。在密码学中,“新”是“坏”的另一个名称。

最好的加密算法是仍然存在的旧算法没有更好的安全标准。从这个意义上说,SHA-2 比 SHA-3 更好,并且在未来也将如此,直到发现这些函数存在问题。NIST 人员自己明确推荐 SHA-2,并否认 SHA-3 比 SHA-2 更好的任何建议。

您可以使用其他密钥派生函数,例如 scrypt 或 pbkdf2+sha2(或 sha3)。

虽然我认为这种身份验证方法非常浪费带宽、内存和 CPU,而且最不安全。为了使密钥拉伸有效,它必须很重,并且您需要为每个请求进行此计算。我认为你最好发布一个加密随机数,并使用像 memcachd 这样的快速非关系数据库来支持服务器状态。当然它不是“RESTful”,但它是一种更高效、更安全的设计。

最好的密码学家在别无选择时使用密码术。计划失败。

听起来您正在尝试维护会话状态,而没有在服务器端存储会话数据的常用方法。

这是一本更好的食谱:

  1. 验证用户名和密码(这里可以使用 bcrypt 进行密码验证)
  2. 将您要存储的所有会话信息放入序列化的 blob 中
  3. 使用经过身份验证的加密,使用强大的秘密服务器密钥加密 blob
  4. 在 cookie/隐藏表单字段/...中发送 blob(base64 编码)

在下一次调用时:

  1. 解密/验证 blob
  2. 反序列化会话数据
  3. 利润!

所有这些步骤都经过了实战考验。