bcrypt(strtolower(hex(md5(pass)))) 可以存储密码吗?

信息安全 密码 哈希 md5 bcrypt
2021-09-04 15:28:36

我有一个存储密码的大型数据库strtolower(hex(md5(pass)))(这是存储密码的不好方法,容易出现彩虹表,字典攻击便宜,没有盐等),我的任务是从 md5 切换到 bcrypt,

我必须使用一个 bcrypt 实现,它在 72 个字节后静默截断,并在第一个空字节(以先到者为准)上静默截断,并且bcrypt(strtolower(hex(md5(pass))))不会出现这些问题中的任何一个。

也可以追溯应用 bcrypt 到现有的strtolower(hex(md5(pass)))密码哈希,而不需要每个人重新登录/切换密码。

这是个坏主意吗?我不这么认为,但仍然想听听这个网站要说什么。也许我错过了一些重要的东西。

3个回答

作为密码破解者,我鼓励我的所有目标都使用这种技术。😉

这(可以理解!)似乎是个好主意,但事实证明,针对现实世界的攻击,用 bcrypt包装未加盐的哈希显然比简单地使用 bcrypt 弱

(编辑:首先,要明确一点,比单独好得多bcrypt(md5($pass))-所以md5($pass)这一切都不应该意味着这个方案应该保持原样。)

从实际攻击的角度来看,包装未加盐的哈希是有问题的,因为攻击者可以这样做:

  1. 从泄漏中获取现有的 MD5 密码 -甚至是尚未破解的 MD5
  2. 在用尽简单的攻击后,将这些 MD5 作为“词表”运行在您的bcrypt(md5($pass))语料库中,以识别具有已知 MD5 的未破解 bcrypt
  3. 以更高的速度破解bcrypt 之外的那些 MD5

是的 - 你必须首先发现 bcrypt 中的 MD5。但关键是MD5 可以是其他未破解的 MD5,恰好存在于其他泄漏中,然后您可以以大幅提高的速度对其进行攻击。

这不是理论上的攻击。高级密码破解者一直使用它来成功破解 bcrypt 哈希,否则攻击者将完全无法获得

这种攻击的工作原理对于非专家来说是非常不直观的,因此我强烈鼓励怀疑者尝试真实世界的场景以了解它是如何工作的:

  1. 使用 MD5 散列 6 个字符的随机密码。
  2. 假设这个 MD5 已经出现在其他一些泄露的密码列表中,证明它在某个时候被用作密码。
  3. 尝试用蛮力直接攻击MD5。
  4. 将 MD5 包裹在 bcrypt 中,尝试直接暴力破解。
  5. 攻击相同的 bcrypt-wrapped MD5,但这次假装你还没有破解 MD5,而是使用包含你的 MD5 的泄露 MD5 的“字典”。
  6. 一旦你“发现”你手头有一个 MD5 在你的一个 bcrypt 中,攻击 MD5,然后将生成的明文传递给你的 bcrypt(md5($pass)) 攻击。

再一次,非常不直观,所以玩它(不要因为理解它需要工作而感到难过;在我最终得到它之前,我与 Jeremi Gosney连续一个小时激烈地反对它!)

我不相信这种技术有一个“官方”的名称,但我一直称它为“哈希去壳”或只是“去壳”。

因此,根据用例,包装 bcrypt 具有吸引力的原因是完全可以理解的(例如,超出 72 个字符的 bcrypt 最大值,尽管由于其他原因,这可能会很棘手,包括“空字节”问题),或者迁移现有的哈希。

因此,如果有人需要在 bcrypt 中包装散列,那么对于这个弱点的缓解措施现在应该很清楚了:您的内部散列绝不能出现在任何其他可能对攻击者可用的密码存储系统中。这意味着您必须使内部哈希值全局唯一

对于您的特定用例 - 您需要保留现有哈希 - 有几个选项,包括:

  • 在您的 Web 或 DB 框架中添加全局胡椒- 因此,这允许您轻松迁移现有的 MD5,但该全局胡椒仍然会被盗(但如果您的网络层与您的 DB 层/身份验证分开,这可能是可接受的风险,YMMV);bcrypt($md5.$pepper)
  • 使用HSM基础架构添加全局辣椒(以一种连网络应用程序都看不到的方式存储辣椒,因此不会被盗)
  • 添加额外的每个哈希盐(但你必须以某种方式将其存储在哈希之外,这开始变得棘手并濒临“滚动你自己的加密”领域);
  • 在 bcrypt 层内使用慢速、加盐的哈希算法或 HMAC 对 MD5 进行哈希处理(不推荐,我什至没有资格就如何正确完成提出建议,但这是可能的 - Facebook 正在这样做,但有些非常聪明人们设计的);

有关更多详细信息,包括一些特定场景来说明为什么它比单独的 bcrypt 弱,请参阅我的超级用户回答here这个关于“预散列”密码的 OWASP 指南,它更清楚地支持我的断言,以及Sam Croley讨论的这个谈话技术。

一般来说,密码升级可能很棘手;请参阅 -这个答案Michal Špaček关于密码存储升级策略的页面。

虽然 Royce 的回答是正确的,因为包装的哈希比未包装的纯 bcrypt 哈希弱,但必须注意的是,它们仍然比使用弱哈希算法和无盐的当前实现强得多,因为攻击者必须经过努力单独攻击每个哈希,而不是简单地在整个数据库上使用预先计算的彩虹表。

虽然长期存储包装的哈希可能不是最佳选择,但它(如您所述)是一个很好的解决方案,可以立即升级密码数据库的安全性,而无需强迫所有人更改密码。为避免包装散列的漏洞,您可以在首次登录时将散列升级为未包装散列,如OWASP 所述

另一种方法是使用现有的密码哈希作为更安全算法的输入。例如,如果应用程序最初将密码存储为 md5($password),则可以轻松将其升级为 bcrypt(md5($password))。以这种方式对散列进行分层可以避免知道原始密码的需要,但是它可以使散列更容易破解,如“预散列密码”部分所述。因此,这些哈希值应该在用户下次登录时用用户密码的直接哈希值替换。

从一种散列方案过渡到另一种散列方案并没有什么不寻常的地方,而且是系统有时会做的事情。如果您有一个不太理想的密码散列方案,并且您希望对其进行改进,请构建它以便新密码(包括从那时起重置密码)使用新的散列方案,而旧密码具有旧的散列方案. 您确保记录本身识别正在使用的方案,并且当用户登录时以适当的方式对其进行验证(然后可以转换到新的散列方案)。渐渐地,随着用户的回归和新用户的加入,越来越多的用户将使用更好的哈希算法。

在您的特定情况下,将 md5 包装在 bcrypt 中可以被认为是一种临时解决方案 - 您可以立即将其应用于所有现有散列 - 而新散列可以在没有md5 的情况下使用 bcrypt。

实施后,您可以考虑是否要通过强制更改其帐户的密码来强制旧用户使用新方案,即使他们没有访问该站点也是如此。为此,您需要考虑以下因素:

  • 在强制更改密码之前,您将等待多长时间?例如,您会在实施新计划后给予 12 个月的时间吗?您当前的计划是否存在特定威胁、漏洞利用或问题意味着应该尽快完成?

  • 对于返回用户和被要求更改密码的用户,您是否需要使用其他方法(例如电子邮件)进行验证?如果您担心当前的密码哈希可能已经或已经被泄露,您可能需要尝试使用验证联系方法进行验证。

  • 在转换到新的哈希方案时,您会允许用户再次使用相同的密码吗?从技术上讲,您可以让他们使用他们现有的密码登录,然后默默地更新哈希值,这样同一个密码就有一个新的哈希值。但是,如果您对旧方案的安全性有特别的担忧,您可能会认为旧密码已泄露且不可重复使用。

请注意,crypt() 本身将其散列设计为可在算法和轮数方面进行扩展,因此未来对任何一个的更改都可以对您透明,旧的散列继续如上所述工作,并以标准化的方式来判断哪个算法和编号任何给定哈希使用的轮数。因此,一旦您转到“$2y$”... crypt 序列化哈希,未来的开关(如果它们可以以该格式实现)会更容易。


其他答案已经解决了 bcrypt-wrapped-md5 是否是一个好主意,我只会补充一点,虽然它可能不会立即破坏一个想法,但它仍然不理想,并且尽快摆脱它是个好主意。

我的问题是非 bcrypt 包装的哈希可能仍然存在于备份、旧服务器中,甚至已经落入坏人之手。如果密码没有改变,那么无论破解 bcrypt 包装的密码需要做多少工作,任何遇到裸 md5 散列的旧转储的人都可以绕过它。因此,这并不理想,在这种情况下,可能需要在适当的时间更改密码。