如何阻止对Web应用程序发起的DoS攻击?

当我在 Heroku 管理安全团队时,我经常做一个噩梦:

我的 PagerDuty 警报响了,提醒我发生了安全事故 。在梦中,我盯着手机并意识到“不,大事不好”——接着,我就被惊醒了 。
我仍然不确定梦中的安全事故到底是什么,但它很可能是 DoS 攻击 。虽然 DoS 攻击简单,但是它造成的影响却可能是毁灭性的:攻击者以超过服务器负载的方式向你的应用程序发送流量 。这虽然不像远程代码执行或数据泄露那样糟糕,但还是相当可怕 。如果客户不能使用你的应用程序,你就会失去他们的信任,损失金钱 。
通常,我们讨论两种类型的 DoS 攻击:
  • “一般的”DoS 攻击,即单台机器就足以导致宕机 。这种攻击的一个经典老派的版本是 zip 炸弹:攻击者诱使你的服务器打开一个精心编制的 ZIP 文件,这个文件被压缩得很小,但是解压后,它却会把你的整个磁盘空间给塞满 。
  • DDoS(分布式拒绝服务)攻击 。这种攻击需要攻击者从多台机器向你的站点发送超大流量 。通常,这些攻击来自僵尸网络——被攻击者控制的大量机器 。这些僵尸网络可以从互联网的特定角落购买,让任何一个有信用卡的人都能进行一次不错的 DDoS 攻击 。
从事 Web 应用程序工作的工程师经常会遇到可用于 DoS/DDoS 攻击的漏洞 。
不幸的是,业界对于如何处理这些漏洞存在广泛分歧 。这种风险很难分析:我曾见过开发团队为如何处理一个 DoS 问题争论了好几个星期 。
本文将试图理清这些分歧,为工程和应用程序安全团队提供了一个框架来考虑 DoS 风险,将 DoS 漏洞分为高、中、低三级,并在每一级提出了缓解措施的建议 。这篇文章的主要关注点是大局,应该适用于任何类型的 Web 应用程序 。但为了具体化,我加入了一些 Django 相关的具体示例 。(我创建了这个框架,因此我对它非常熟悉 。)
评估 DoS 风险在应用层评估 DoS 漏洞的风险可能很难 。安全专家间存在着广泛分歧:你经常会看到 2 个不同的应用程序安全团队对相似问题的处理方式是截然不同的 。
有人认为:要完全抵御集中式 DDoS 攻击,这几乎是不可能的——一个足够专注的攻击者可以向你投放比你的应用程序能处理的更多的带宽 。如果没有上游网络提供商(例如 Cloudflare)提供用来防护机器人程序攻击的特定工具,你永远无法完全缓解 DDoS 攻击 。
因此,追踪和修复假设的 DoS 漏洞似乎是在浪费开发人员的时间 。这些团队将大部分潜在的 DoS 问题视为可接受的风险,并将精力集中在准备网络级别的缓解措施上 。
另外一些团队指出,传统风险模型有三个潜在问题:机密性、完整性和可用性 。我们都知道,正常运行时间是一个安全问题 。越来越普遍的情况是,攻击者关闭服务,然后要求赎金来停止攻击 。最近针对Garmin 的攻击是一个非常明显的例子;攻击者几乎关闭了Garmin 的所有服务,据报道要求100 万美元的赎金 。(在这个例子中,攻击是勒索软件,但很容易看出DoS 攻击也会有类似的效果) 。因此,DoS 漏洞和其它任何漏洞一样都是风险,它们都应该被缓解 。
重要的是,这两个立场都是合理的!将DoS 视为超出应用程序安全范围是合理的;同样,将其纳入范围也是合理的 。我经常看到安全团队在这两个立场间争论不休 。如果确定不了对错,就不可能找出解决的办法 。
攻击者杠杆理论对于这个争论,我采用的是攻击者杠杆理念 。杠杆会放大力量:在杠杆长的一端施加很小的力量,就会在短的一端被放大数倍 。具体到DoS 攻击,如果一个漏洞有高杠杆率,这意味着攻击者只需要很少的资源就能消耗你的大量服务器资源 。
例如,如果你的Web 应用程序的一个bug 允许单个 GET请求消耗 100% 的 CPU,那么这就是一个非常高的杠杆率 。只需要少量攻击,你的 Web 服务器就会陷入瘫痪 。另一方面,一个低杠杆率的漏洞需要花费攻击者的大量资源,最后才会让可用性降低一点点 。如果一个攻击者必须花费数千美元才能让一台服务器瘫痪,那么你能比他们更快地进行扩展 。
杠杆率越高,风险越高,我就越有可能直接解决这个问题 。杠杆率越低,我就越有可能接受这个风险或者依赖网络级别的缓解措施 。
当然,具体问题需要具体分析 。根据杠杆率,我将 DoS 风险分为高、中、低三个风险级别 。对每个风险级别,我将分析如何识别漏洞属于哪个级别,讨论一些示例,并给出一些缓解建议 。


推荐阅读