防止针对慢散列函数的拒绝服务攻击?

信息安全 密码学 验证 密码管理 拒绝服务
2021-08-09 17:55:37

我最近一直在考虑 bcrypt,我想知道是否有办法处理具有慢散列函数的固有 (D)DoS 问题。也就是说,如果我设置了 bcrypt,所以我的机器需要 100 毫秒来散列密码,那么每秒只需要 10 次密码尝试就可以使我的服务器无响应。我可以降低难度,但我越快为我做,我就越快为攻击者做。我可以禁止任何尝试过多密码的人,但如今僵尸网络无处不在。

这个问题有什么通用的解决方案吗?

我在想可能有一些策略可以让客户做一些复杂但容易验证的事情,并在检查密码之前检查结果,但它有几个问题:

  • 有没有办法用纯 HTML(没有 JavaScript)来做到这一点。
  • “复杂的东西”会是什么?RSA 加密让我想到了生成两个素数,将它们相乘,然后询问客户原始素数是什么,但我怀疑“生成两个素数”步骤也不会特别快。

我考虑过的另一件事是将 bcrypt 结果缓存在内存中,因此对一个用户的攻击不会引起问题,但是如果我有太多用户而无法合理地做到这一点怎么办?

我主要考虑在网站上的用户名/密码登录框的上下文中使用它,但如果没有更好的方法,任何处理这个问题的方法都可能在 JavaScript 中完成。

编辑:

以某种方式限制请求的想法很有用,但这不是我想到的那种解决方案。它使 DDoS 攻击更加昂贵(因为您需要更多的 IP 地址),但它并没有解决服务器所做的工作比攻击者多得多的根本问题。

我正在考虑的一个例子是使用加盐密码如何使彩虹表比简单的暴力攻击更糟糕所以如果你使用盐,你根本不必担心彩虹表,因为有更好的攻击。我想要的是一种攻击我的登录页面的方法,而不是仅仅点击随机 URL。

4个回答

一种可能性是考虑使用客户谜题基本思想是在您接受用户名/密码对并尝试验证之前,强制客户端做大量工作,并证明它已经这样做了。

以下是该方法的概述。服务器生成一个随机拼图,并将拼图发送给客户端。服务器生成谜题的方式可以合理地预测解决谜题需要多少努力(例如,100 毫秒的计算)。客户端解决难题,然后将解决方案与用户的用户名和密码一起发送。

在网络设置中,这可能会使用 Javascript 实现:登录页面将包含用于解决难题的 Javascript 代码和难题描述。合法用户的网络浏览器将在页面上运行 Javascript,这将解决难题并将解决方案与用户名和密码一起包含在表单中。

这使得攻击者更难对服务器进行 DOS 操作。例如,如果服务器需要 100 毫秒的计算时间来检查用户密码的有效性,并且如果服务器选择一个需要 100 毫秒来解决的谜题,那么攻击者至少需要做与服务器一样多的工作。如果你转动拨盘来选择需要 200 毫秒才能解决的谜题,那么现在攻击者必须做两倍于你的工作才能对你进行 DOS。因此,这并不能阻止 DOS,但它会使它变得更加困难,并且需要攻击者投入更多资源——这可能会在一定程度上降低风险。

这是一个非常简单的示例,说明如何构建拼图。拼图的描述是一个随机的 128 位数字R(由服务器选择,并发送给客户端)。目标是找到一个数字n,使得 SHA256( R , n ) 在其低 20 位为零。客户端可以遍历 n 的值测试n = 0 是否为解决方案,测试n = 1 是否为解决方案,测试n = 2 是否为解决方案,...)直到客户端找到解决方案。平均而言,您应该预计这需要大约 2 20 次试验。如果客户端每秒可以执行大约一千万次哈希,那么 2 20试验大约需要十分之一秒(100 毫秒)。一旦客户端找到解决方案,客户端就可以将n发送到服务器。请注意,服务器可以快速验证n是该难题的有效解决方案(它只需要一个哈希,而不是一百万个哈希)。这创建了所需的不对称性,服务器可以非常快速地生成新的谜题并验证声称的谜题解决方案,但解决谜题需要更长的时间,并且服务器可以以相当准确的程度控制解谜所需的时间。这只是拼图结构的一个例子;对这个问题进行了大量的研究,并且有更好的 构造

你可能对kaPoW特别感兴趣,它为 web 上下文实现了这个想法。

主要限制是这对于专门的攻击是不安全的。一个严重的攻击者只需获得一个由几百台机器组成的僵尸网络(这并不昂贵),然后对你进行 DOS 攻击,现在你完全被淹没了。客户端谜题无法阻止这种攻击:它们只能阻止相对低级的攻击。然而,停止 DOS 通常是非常困难的。

第二个限制与可能访问您网站的客户的计算能力的广泛可变性有关。高端台式机可以在 100 毫秒内解决的难题可能需要移动设备 1000 毫秒才能解决(我在这里编数字)。如果您选择的参数可以在可能访问您网站的最低功耗设备上相对容易地解决这个难题,那么您可能会发现针对高端台式机的安全性会降低。此外,C 程序(由攻击者运行)可能比 Javascript(由合法用户运行)更快地解决难题似乎是合理的,这将进一步加剧攻击者解决难题的速度与解决难题的速度之间的差距。最慢的合法客户端可以多快解决它们。这种差距会降低安全性。

第三个限制是客户端谜题需要客户端使用 Javascript 才能登录。但是这可能是可以接受的。

这个想法的一个变种是让服务器监控自己并检查它是否处于高负载状态。在正常情况下,跳过拼图。但是,如果服务器似乎受到 DOS 攻击,则开始要求登录尝试以提供谜题解决方案。这减轻了客户端谜题方法的一些缺点,但仍远非完美,并且仍然无法承受严重的 DOS 攻击。

我描述了这个潜在的解决方案,以防你感兴趣,但我并不真的推荐它用于大多数网站。对于大多数网站来说,DOS的风险都比较低,如果确实发生了,你可以在当时进行处理。出于这个原因,我的猜测是,实施这些防御措施可能不值得您花费时间或精力。如果您有那么多额外的时间和精力可以腾出,您最好将其用于构建新功能或使您的网站更好。

减少每分钟的猜测次数是可行的方法,但sleep在代码中实现它仍然会消耗宝贵的资源并导致拒绝服务。

相反,您希望客户端等待,而不是服务器。因此,记录尝试的时间,在该时间上添加X几秒钟,并且在该时间到期之前不要尝试从该用户/IP 进行进一步的身份验证。编辑,添加:即,如果接收到的认证尝试,并显示错误消息拒绝它甚至不用检查密码。

您需要在登录页面中放置一些相应的 javascript,以便用户在超时到期之前不会提交新的登录尝试(加上安全余量)。这可以防止用户看到不必要的错误消息并感到困惑。

每次尝试增加超时时间也是一个好主意。这有助于避免给“普通”用户带来不便,同时使暴力攻击几乎不可能。

您可以考虑的另一种方法是使用验证码使这种 DOS 攻击更加困难。基本思想是要求用户在登录时解决验证码,并要求他们在验证他们的密码之前向验证码提供正确的解决方案。

这对用户不友好,所以如果你想考虑这个,我会推荐一个变体。让您的服务器监控自己的负载,以便它可以检测到它何时受到攻击。(例如,测量你在 bcrypt-hash 密码上花费了多少 CPU 时间;或者只是测量服务器上的负载。)在正常情况下,不要做任何特别的事情:用户只需要像往常一样提供用户名和密码,任何地方都没有验证码。

当您检测到您可能受到攻击时,请进入自我保护模式。在这种模式下,服务器应该在登录页面发送一个验证码,要求用户输入他们的用户名,输入他们的密码,然后输入验证码的解决方案。当用户点击提交时,服务器应先验证解 CAPTCHA,然后再使用 bcrypt 验证用户密码。如果用户没有正确解决验证码,甚至不要尝试验证他们的密码。

这提高了 DOS 攻击的门槛,因为现在攻击者不能简单地每秒发送 10 个无效的用户名/密码对:攻击者还必须每秒向 DOS 服务器解决 10 个验证码。结果,普通的 DOS 攻击不再成功。此外,这提供了 DOS 抵抗力,同时确保在大多数情况下用户永远不需要看到验证码:用户只需要在服务器受到攻击时解决验证码。

当然,如果您愿意,您可以将此方案与 IP 地址和用户名的速率限制相结合。

也就是说,该方案的安全性远非完美。攻击者使用地下市场来自动为您解决验证码的过程,但需要付费。这些服务提供了一个 API,客户可以使用该 API 提交验证码并取回解决方案。行进每解决一千个验证码就需要几美元。在此基础上,我们可以计算通过使用这些 CAPTCHA 解决服务成功发起针对该方案的 DOS 攻击的成本。攻击者每天需要解决 864,000 个验证码(每秒 10 个)才能关闭您的服务器,使用现有市场每天的停机时间将花费数千美元。这是成功发起 DOS 攻击的成本上限。然而,几乎可以肯定它大大高估了对服务器进行 DOS 攻击的成本,因为几乎可以肯定会有其他更经济的方法在服务器上进行 DOS 攻击。

无论如何,我基于使用验证码分享这个潜在的解决方案,但我并不真的推荐它用于大多数网站。对于大多数网站来说,DOS的风险都比较低,如果确实发生了,你可以在当时进行处理。出于这个原因,我的猜测是,实施这些防御措施可能不值得您花费时间或精力。如果您有那么多额外的时间和精力可以腾出,您最好将其用于构建新功能或使您的网站更好。

目前,DDOS 是关闭服务器的最简单和最便宜的方法。我已经看到 DDOS 攻击的成本估计在每天 100 美元的范围内,尽管击败更大、资源更好的目标可能要花费更多。

保护您的服务器免受 DOS 攻击并不容易它的经济学对你不利。也许您可以做的最好的事情之一就是让您的站点拥有大量的过剩容量,并确保它可以随着流量的增长而扩展(也许使用具有虚拟机和按需网络容量的云服务)。这种方法的优点是即使您从未受到 DOS 攻击也是有益的,因为它可以帮助您避免被大量合法用户淹没——例如,如果您被 Slashdotted。

您可以做的是在每次登录失败时强制请求休眠一个指数时间。

第一次登录保持一秒钟;第二次失败 2 秒;第三次失败 4 秒等。或者更长或更短,具体取决于您的服务器可以处理的内容。它不会伤害输错密码的普通用户,但它会让任何尝试暴力破解的人在几次失败后等待很长时间。