在互联网中一谈起DDOS攻击,人们往往谈虎色变。DDOS攻击被认为是安全领域最难解决的问题之一,迄今为止也没有一个完美的解决方案。各个互联网公司都等着5G时代的来临,等它来临分物联网领域的一份羹。当物联网时代真正来临的时候,网络设备数量会呈指数性地增长,对DDOS攻击的防御确实带来了一个很大的威胁。
一.DDOS简介
DDOS又称为分布式拒绝服务攻击,全称是Distributed Denial os Service。DDOS本是利用合理的请求造成资源过载,导致服务不可用。比如一个停车场总共有100个车位,当100个车位都停满车后,再有车想要停进来,就必须等已有的车先出去才行。如果已有的车一直不出去,那么停车场的入口就会排起长队,停车场的负荷过载,不能正常工作了,这种情况就是“拒绝服务”。
我们的系统就好比是停车场,系统中的资源就是车位。资源是有限的,而服务必须一直提供下去。如果资源都已经被占用了,那么服务也将过载,导致系统停止新的响应。
分布式拒绝服务攻击,将正常请求放大了若干倍,通过若干网络节点同时发起攻击,以达成规模响应。这些网络节点往往是黑客们所控制的“肉鸡”,数量达到一定规模后,就形成了一个“僵尸网络”。大型的僵尸网络,甚至达到了数万、数十万台的规模。如此规模的僵尸网络发起的DDOS攻击,几乎是不可阻挡的。
常见的DDOS攻击有SYN flood、UDP flood、ICMP flood等。其中SYN flood是一种最为经典的DDOS攻击,其发现于1996年,但是至今仍然保持着非常强大的生命力。SYN flood如此猖獗是因为它利用了TCP协议设计中的缺陷,而TCP/IP协议是整个互联网的基础,牵一发而动全身,如今想要修复这样的缺陷几乎成为不可能的事情。
在正常情况下,TCP三次握手过程如下:
(1)客户端向服务器发送一个SYN包,包含客户端使用的端口号和初始序列号x
(2)服务器端收到客户端发送来的SYN包后,向客户端发送一个SYN和ACK都置位的TCP报文,包含确认号x+1和服务器端的初始序列号y;
(3)客户端收到服务器端返回的SYN+ACK报文后,向服务器端返回一个确认号为y+1、序号为x+1的ACK报文,一个标准的TCP连接完成。
而SYN flood在攻击时,首先伪造大量的源IP地址,分别向服务器端发送大量的SYN包,此时服务器端会返回SYN/ACK包,因为源地址时伪造的,所以伪造的IP并不会应答,服务器端没有收到伪造IP的回应,会重试3-5次并且等待一个SYN Time(一般为30秒至2分钟),如果超时则丢弃这个连接。攻击者大量发送这种伪造源地址的SYN请求,服务端将会消耗非常多的资源(CPU和内存)来处理这种半连接,同时还要不断地对这些IP进行SYN+ACK重试,最后的结果是服务器无暇理睬正常的连接请求,导致拒绝服务。
对抗SYN flood的主要措施有SYN Cookie/SYN Proxy、safereset等算法。SYN Cookie的主要思想是为每一个IP地址分配一个“Cookie”,并统计每个IP地址的访问频率。如果在短时间内收到大量的来自同一个IP地址数据包,则认为受到攻击,之后来自这个IP地址的包将被丢弃。
在很多对抗DDOS的产品中,一般会综合使用各种算法,结合一些DDOS攻击的特征,对流量就行清洗,对抗DDOS的网络设备可以串联或者并联在网络出口处。
但是DDOS仍然是业界的一大难题,当攻击流量超过了网络设备,甚至带宽的最大负荷时,网络仍将瘫痪。一般来说,大型网站之所以看起来比较能“抗”DDOS攻击,是因为大型网站的带宽比较充足,集群内服务器的数量也比较多。但一个集群的资源毕竟是有限的,在实际的攻击中,DDOS的流量甚至可以达到数G到几十G,遇到这种情况,只能与网络运营商合作,共同完成DDOS攻击的响应。
二.应用层DDOS
应用层DDOS,不同于网络层DDOS,由于发生在应用层,因此TCP三次握手已经完成,连接已经建立,所以发起攻击的IP地址也都是真实的。但应用层DDOS有时甚至比网络层DDOS攻击更为可怕,因为今天几乎所有的商业Anti-DDOS设备,只在对抗网络层DDOS时效果好,而对应用层DDOS攻击却缺乏有效的对抗手段。
那么应用层DDOS到底是怎么一回事呢?这就要从“CC攻击”说起了。
1.CC攻击
“CC攻击”的前身是一个叫fatboy的攻击程序,当时黑客为了挑战绿盟的一款反DDOS设备开发了它。绿盟是中国著名的安全公司之一,它有一款叫“黑洞(Collapasar)”的反DDOS设备,能够有效地清洗SYN Flood等有害流量。而黑客则挑衅地将fatboy所实现的攻击方式命名为:Challenge Collapasar(简称CC),意指在黑洞的防御下,仍然能有效完成拒绝服务攻击。
CC攻击的原理非常简单,就是对一些资源消耗较大的应用页面不断发起正常的请求,以达到消耗服务端资源的目的,在Web应用中,查询数据库、读/写硬盘文件等操作,相对都会消耗比较多的资源。在百度百科中有一个很典型的例子,应用层常见SQL代码规范如下(以PHP为例):
翻页频繁,$start数字急剧增加时,查询影响结果集=$start+30,该查询效率呈现明显下降趋势,而多并发频繁调用,因查询无法立即完成,资源无法立即释放,会导致数据库请求连接过多,数据库阻塞,网站无法正常工作。
在互联网中充斥着各种搜索引擎、信息收集等系统的爬虫(spider),爬虫把小网站直接爬死的情况时有发生,这与应用层DDOS攻击的结果很像。由此看来,应用层DDOS攻击与正常业务的界限比较模糊。
应用层DDOS攻击还可以通过以下方式完成:在黑客入侵了一个流量很大的网站后,通过篡改页面,将巨大的用户流量分流到目标网站。比如,在大流量网站siteA上插入一段代码:
那么所有访问该页面的siteA用户,都将对此target发起一次HTTP GET请求,这可能直接导致target拒绝服务。
应用层DDOS攻击是针对服务器性能的一种攻击,那么许多优化服务器性能的方法,都或多或少地能缓解此种攻击。比如将使用频率高地数据放在memcache中,相对于查询数据库所消耗的资源来说,查询memcache所消耗的资源可以忽略不计,但很多性能优化的方案并非是为了对抗应用层DDOS攻击而设计的,因此攻击者想要找到一个资源消耗大的页面并不困难。比如当memcache查询没有命中时,服务器必然会查询数据库,从而增大服务器资源的消耗,攻击者只需要找到这样的页面即可。同时攻击者除了触发“读”数据操作外,还可以触发“写”数据操作,“写”数据的行为一般会导致服务器操作数据库。
2.限制请求频率
最常见的针对应用层DDOS攻击的防御措施,是在应用中针对每个“客户端”做一个请求频率的限制。通过IP地址和Cookie定位一个客户端,如果客户端的请求在一定时间内过于频繁,则对之后来自该客户端的所有请求都重定向到一个出错页面。从架构上来看,这样的防护措施需要放在业务逻辑之前,才能起到保护后端应用的目的,可以看做是一个“基层”的安全模块。
3.道高一尺,魔高一丈
然而这种防御方法并不完美,因为它在客户端的判断依据上并不是永远可靠的。这个方案中有两个因素用以定位一个客户端:一个是IP地址,另一个是Cookie。但用户的IP地址可能会发生变化,而Cookie又可能会被清空,如果IP地址和Cookie同时都发生了变化,那么就无法再定位到同一个客户端了。
如果让IP地址发生变化呢?使用“代理服务器”是一个常见的做法。在实际的攻击中,大量使用代理服务器或傀儡机来隐藏攻击者的真实IP地址,已经成为一种成熟的攻击模式。攻击者使用这些方法可不断地变换IP地址,就可以绕过服务器对单个IP地址请求频率的限制了。
代理猎手是一个常用的搜索代理服务器的工具:
而AccessDiver则已经自动化地实现了这种变换IP地址的攻击,它可以批量导入代理服务器地址,然后通过代理服务器在线暴力破解用户名和密码。
攻击者使用的这些混淆信息的手段,都给对抗应用层DDOS攻击带来了很大的困难。那么到底如何解决一个问题呢?应用层DDOS攻击并非是一个无法解决的问题,一般来说,我们可以从以下几个方面着手:
首先,应用代码要做好性能优化。合理地使用memcache就是一个很好的优化方案,将数据库的压力尽可能转移到内存中。此外还需要及时地释放资源,比如及时关闭数据库连接,减少空连接等消耗。
其次,在网络架构上做好优化。善于利用负载均衡分流,避免用户流量集中在单台服务器上,同时可以利用好CDN和镜像站点的分流作用,缓解主站的压力。
最后,也是最重要的一点,实现一些对抗手段,比如限制每个IP地址的请求频率。
下面继续更深入地探讨还有哪些方法可以对抗应用层DDOS攻击。
三.验证码的那些事儿
验证码是互联网常用的技术之一,它的英文简称是CAPTCHA(Completely Automated Public Turing Test to Tell Computers and Human Apart,全自动区分计算机和人类的图灵测试)。在很多时候,如果可以忽略对用户体验的影响,那么引入验证码这一手段能够有效地阻止自动化的重放行为。
如下是一个用户提交评论的页面,嵌入验证码能够有效防止资源滥用,因为通常脚本无法自动识别出验证码。
但验证码也分三六九等,有的验证码容易识别,有的则较难识别。
CAPTCHA发明的初衷是为了识别人和机器。但验证码如果设计得过于复杂,那么人也很难辨识出来,所以验证码是一把双刃剑。
有验证码,就会有验证码破解技术。除了直接利用图像相关算法识别验证码外,还可以利用Web实现上可能存在的漏洞破解验证码。
因为验证码的验证过程,是比对用户提交的名文和服务器端Session里保存的验证码明文是否一致。所以曾经有验证码系统出现过这样的漏洞:因为验证码消耗掉后SessionID未更新,导致使用原有的SessionID可以一直重复提交同一个验证码。
还有的验证码实现方式,是提前将所有的验证码图片生成好,以哈希过的字符串作为验证码图片的文件名。在使用验证码时,则直接从图片服务器返回已经生成好的验证码,这种设计原本的想法是为了提高性能。
但这种一一对应的验证码文件名会存在一个缺陷:攻击者可以事先采用枚举方式,遍历所有的验证码图片,并建立验证码到名文之间的一一对应关系,从而形成一张“彩虹表”,这也会导致验证码形同虚设。修补的方式是验证码的文件名需要随机化,满足“不可预测性”原则。
随着技术的发展,直接通过算法破解验证码的方法也变得越来越成熟。通过一些图像处理技术,可以将验证码逐步变化成可识别的图片。
四.防御应用层DDOS
验证码不是万能的,很多时候为了给用户一个最好的体验而不能使用验证码,且验证码不宜使用过于频繁,所以我们还需要有更好的方案。验证码的核心思想是识别人和机器,那么顺着这个思路,在人机识别方面,我们是否还能再做一些事情呢?答案是肯定的。
在一般情况下,服务器端应用可以通过判断HTTP头中的User-Agent字段来识别客户端。但从安全性来看这种方法并不可靠,因为HTTP头中的User-Agent是可以被客户端篡改的,所以不能信任。
一种比较可靠的方法是让客户端解析一段JavaScript,并给出正确的运行结果。因为大部分的自动化脚本都是直接构造HTTP包完成的,并非在一个浏览器环境中发起的请求。因此一段需要计算的JavaScript,可以判断出客户端到底是不是浏览器。类似的,发起一个flash让客户端解析,也可以起到同样的作用。但需要注意的是,这种方法并不是万能的,有的自动化脚本是内嵌在浏览器的“内挂”,就无法检测出来了。
除了人机识别外,还可以在Web Server这一层做些防御,其好处是请求尚未到达后端的应用程序里,因此可以起到一个保护的作用。
在Apache的配置文件中,有一些参数可以缓解DDOS攻击。比如调小Timeout、KeepAliveTimeout值,增加MaxClients。但需要注意的是,这些参数的调整可能会影响到正常应用,因此需要视情况而定。Apache提供的模块接口为我们扩展Apache、设计防御措施提供了可能。目前已经有一些开源的Module全部或部分实现了针对应用层DDOS攻击的防护。