为什么从现有的散列函数中即兴创作你自己的散列函数是如此糟糕

信息安全 密码 哈希 bcrypt 加密
2021-09-05 05:15:23

恐怕我会因为问这个老问题而被人扔西红柿,但是就这样吧。

在阅读了一遍又一遍地从现有的散列函数中编写自己的密码散列是危险的之后我仍然不明白其中的逻辑。这里有些例子:

  • md5(md5(salt) + bcrypt(password))
  • scrypt(bcrypt(password + salt))
  • sha1(md5(scrypt(password + md5(salt))))

反对这些的典型论据如下:

你不是密码学家!您不知道这些哈希是否更安全。把它留给知道他们在做什么的专家。这些不会增加额外的安全性。

诚然,他们没有将函数改进为哈希(即,使其更难逆转或发现冲突等),但他们肯定不会让哈希变得更糟吗?如果他们这样做了,那么黑客将能够在他们认为合适的情况下将标准散列密码重新散列到这些古怪的散列中并削弱散列?我不买。

第二个论点:

Kerckoffs 原则:即使系统的一切都已知,密码系统也应该是安全的。

同意。这基本上是首先不将密码存储为明文的动机。但是,如果我对第一个批评的回应成立,那么这些古怪的散列仍然可以作为安全散列,并且我们的系统不会像使用标准散列那样违反 Kerckoffs 的原则。

以下是使用“古怪”散列而不是普通散列的两个可能(据我所知,这是值得的)优势:

  1. 当然,如果攻击者拥有源代码,您的系统应该是安全的,但很有可能您的攻击者无法访问您的源代码,并且可能无法猜测您古怪的哈希值,从而进行任何暴力尝试强行不可能。
  2. (这是我提出这个问题的真正动机)BCrypt被认为是安全的,对 CPU 和 GPU 来说很难(很棒),但使用专用硬件可以非常快SCrypt据说很难在 CPU、GPU 和当前可用的专用硬件上进行暴力破解,但由于缺乏曝光度,它是较新的并且不像 BCrypt 那样受到加密社区的信任。但是哈希不是BCrypt(SCrypt(password + salt))两全其美吗?

我很欣赏大多数对这些自制散列的咆哮背后的激情/愤怒来自普通程序员对什么是好的散列缺乏了解,并且担心鼓励这种古怪的散列将不可避免地以弱和无用而告终哈希进入生产代码。但是,如果古怪的哈希是由可靠且受信任的哈希精心构建的,那么安全性的收益难道不是非常有价值和真实的吗?


更新

我得到了一堆很好的答案,谢谢。我的假设似乎忽略了一点,虽然组合哈希不能更容易破解原始密码并因此破解组成哈希,但两个或多个安全哈希的组合可以 - 至少在原则上 - 更弱由于它们之间未经研究且复杂的交互,因此比其任何一个内部哈希值都要高。这意味着可以找到一些通过古怪散列的字符串,而不必破坏组成它的散列。

4个回答

你需要问这个问题的事实就是答案本身——你不知道堆叠这些原语有什么问题,因此不可能知道有什么好处或缺点。

让我们对您提供的每个示例进行一些分析:

md5(md5(salt) + bcrypt(password))

我可以在这里看到一些问题。首先是您正在对盐进行 MD5 处理。这有什么好处?没有任何。它增加了复杂性,并且盐只是为了防止密码冲突和预计算(例如彩虹表)攻击而唯一。在这里使用 MD5 没有任何意义,并且实际上可能会削弱该方案,因为 MD5 已经知道微不足道的冲突。因此,这里引入 MD5 的可能性很小,这可能意味着两个唯一的盐产生相同的 MD5 哈希,从而导致有效地复制盐。那很糟。

接下来,对密码使用 bcrypt。行。好吧,大多数 bcrypt 实现在内部都需要盐,所以这在技术上已经是无效的。假设您知道这一点,并且您的意思是说bcrypt(md5(salt), password)这部分仍然落入我上面描述的弱点,但它并不算太破旧 - 删除 MD5 并且它是 bcrypt 的标准使用。

最后,你 MD5 整个事情。你为什么做这个?目的是什么?它带来什么好处?据我所知,根本没有任何好处。在不利方面,它增加了更多的复杂性。由于大多数 bcrypt 实现都使用该$2a$rounds$salt$hash符号,因此您将不得不编写代码来解析它,以便您可以提取哈希部分并将其余部分单独存储。您还需要一个 MD5 实现,这是不必要的。

因此,就潜在攻击向量的代码足迹而言,您已经从简单的 bcrypt 实现转变为具有自定义解析代码和 MD5 实现的 bcrypt 实现,以及一些将它们粘在一起的胶水代码。对于零收益,以及盐处理中的潜在漏洞。

下一个:

scrypt(bcrypt(password + salt))

这个还不错,但是您再次需要一些代码来分别将结果解析bcrypt为哈希和盐/轮数。在这种情况下,我会有一点好处,因为 bcrypt 和 scrypt 以不同的方式工作以实现大致相同的目标,这将使资金雄厚的攻击者更难以构建自定义 ASIC 来破解你的方案。但这真的有必要吗?真的会遇到一个民族国家会投入几百万美元来破坏你的哈希的情况吗?而且,如果这种情况出现,攻击者是否真的会因为不得不额外花费几百万来使他们的筹码数量翻倍而烦恼?

像这样结合 bcrypt 和 scrypt 的另一个潜在问题是,很少有人研究这两者如何交互。因此,我们不知道是否有任何奇怪的情况会导致问题。作为一个更明显的例子,以一次性垫为例。我们计算c=m^k一些消息m和一些同样长的完全随机密钥k,我们得到了完美的安全性。所以让我们做两次,以获得更高的安全性!这给了我们c=m^k^k......哦,等等,这只是给了我们m因此,由于我们没有花时间正确理解系统内部的工作原理,我们最终发现了一个真正的安全漏洞。显然,在 KDF 的情况下它更复杂,但同样的原理也适用。

最后:

sha1(md5(scrypt(password + md5(salt))))

我们再次遇到了 MD5 的盐问题。我也对 MD5'ing SHA1 哈希很感兴趣。如果您已经在使用像 scrypt 这样的慢速 KDF,那会有什么好处?与计算密码的 scrypt 摘要所需的数百毫秒相比,计算这些哈希所需的几纳秒相形见绌。您正在为绝对不相关的“安全”层增加复杂性,这总是一件坏事。您编写的每一行代码都是潜在的漏洞。


现在请记住我在回答开始时提出的观点。如果在这个答案的任何时候,您认为“哦,是的,我没有考虑过”,那么我的观点就得到了证明。

你遇到了我所说的Dave的错误格言:

如果我添加更多加密的东西,它会更安全。

这是开发人员的共同特点,我曾经也相信这一点。它与否认其他原则(例如Kerckhoff 原则)密切相关。最终,你必须意识到并接受默默无闻不是安全栏;这是弱加密的拐杖。如果你的加密货币很强大,它不需要拐杖。

加密原语可以安全地堆叠,当且仅当您足够了解原语以了解它们的弱点以及这些弱点如何相互作用时,才能提高安全性。如果您不了解它们,或者不了解细节 - 好吧,这就是您获得Dave 协议的方式

问题是很少有人对它们都了解得足以判断某种组合是否安全。这就是为什么它需要被发布和审查的原因,如果它没有被审查,你就无法知道它是否像 CRC32 一样强大scrypt或是否更接近 CRC32。

所以,如果你不是专家——很可能你有一些比你用过的最弱的原语更弱的东西(参见 Dave 的协议),你不会知道它。或者至少在它被破解之前你不会知道它——在 Pastebin 上找到你的用户密码并不是确定该方案有缺陷的理想方法。

我确实同意,从深度防御的角度来看,某种程度的隐蔽性会有所帮助,但底层系统必须是安全的。

之间scryptbcryptPBDKF2- 几乎每个平台都将支持其中至少一个。这些是众所周知的并且经过充分测试 - 它们提供不同级别的保护,但它们仍然比奇怪的堆叠安全得多md5sha1

对于您结合 scrypt 和 bcrypt 的具体问题,请记住,这些函数具有可配置的成本,并且您希望尽可能提高该成本,同时保持它对您的特定用途的容忍度。例如,如果您可以使用最多X次迭代的 bcrypt(除此之外,它对于您的服务器和您的平均每秒用户连接数来说太贵了),或者最多可以使用Y次迭代的 scrypt,那么您就不能使用 scrypt(bcrypt) bcrypt进行X次迭代,然后scrypt 进行 Y次迭代:这将超出您的 CPU 预算。

因此,如果你级联 scrypt 和 bcrypt,那么你必须使用比单独使用一个更少的迭代。您不会通过简单地将它们串在一起来“两全其美”。实际上,您可以期望的最好结果是两者之间的一种平均。这是以更复杂的代码为代价的,这在谈论安全性(或者,就此而言,可维护性)时本质上是不好的。

除了亚当的回答,我还想提一下,任何时候你使用密码学,你都应该有一个强有力的、不可避免的理由这样做。在您上面的示例中,这不存在。

md5(md5(salt) + bcrypt(password))
scrypt(bcrypt(password + salt))

算法已经足够强大,并且被认为是牢不可破的bcrypt你想解决什么问题?为什么你相信结合他们的结果(特别是与)会解决这个问题?在最好的情况下,您可能只是将破解密码的难度降低到最弱的哈希值,而不是真正提高安全性。最坏的情况是可怕的不确定。scryptmd5

md5(sha1(md5(md5(password) + sha1(password + salt)) + password))

这个解决方案更糟糕。它手动实现了重复的哈希方案,但没有足够的轮次来实际对攻击者施加重要的工作因素。

简而言之,问题在于:

  • 您正在使用密码学而实际上没有需要解决的问题
  • 您大大增加了在实施中引入缺陷的可能性
  • 您可能已经降低了最弱哈希算法的安全性,并且
  • 你介绍了一个未知的最坏情况,以前不存在