DDOS,Distributed Denial of Service,分布式拒绝服务。至今没有一个完美的解决方案。DDOS本是利用合理的请求造成资源过载,导致服务不可用。
常见的DDOS攻击有SYN flood、UDP flood、ICMP flood等。其中SYN flood最经典,其利用TCP协议设计中的缺陷,而TCP\IP协议是整个互联网的基础,所有想修复这种缺陷就需要牵一发而动全身,很困难。
SYN flood:
首先伪造大量的源IP地址,分别向服务器端发送大量的SYN包,此时服务前端会返回SYN\ACK包,因为源地址是伪造的,所以伪造的IP并不会应答,服务器端没有收到伪造IP的回应,会重试3~5次并且等待一个SYN Time(一般为30秒至两分钟),如果超时则丢弃这个连接。攻击者大量发送这种伪造源地址的SYN请求,服务器端将会消耗非常多的资源(CPU和内存)来处理这种半连接,同时还要不断地对这些IP进行SYN+ACK重试。最后的结果是服务器无暇理睬正常的连接请求,导致拒绝服务。
对抗SYN flood的主要措施有SYN Cookie/SYN Proxy、safereset等算法。SYN Cookie的主要思想是为每一个IP地址分配一个“Cookie”,并统计每个IP地址的访问频率。如果在短时间内受到大量的来自同一个IP地址的数据包,则认为受到攻击,之后来自这个IP地址的包将被丢弃。
应用层DDOS:
应用层DDOS与网络层DDOS不同,由于发生在应用层,因此TCP三次握手已经完成,连接已经建立,所以发起攻击的IP地址也是真实的。
我们从CC攻击说起:
CC攻击就是对一些消耗资源较大的应用页面不断发起正常的请求,以达到消耗服务端资源的目的。在Web应用中,查询数据库、读/写硬盘文件等操作,相对都会消耗比较多的资源。比如:
$sql="select * from post where tagid='$tagid' order by postid desc limit $start,30";
当post表数据庞大,翻页频繁,$start数字急剧增加时,查询影响结果集=$start+30;该查询效率呈现明显下降趋势,而多并发频繁调用,因查询结果无法立即完成,资源无法立即释放,会导致数据库请求连接过多,数据库阻塞,网站无法正常打开。
应用层DDOS攻击还可以通过以下方法完成:在攻击者入侵了一个流量很大的网站后,通过篡改页面,将巨大的用户流量分流到目标网站。比如,在大流量网站siteA上插入一段代码:
<iframe src="http://target" height=0 width=0></iframe>
那么所有访问该页面的siteA用户,都将对此target发起一次HTTP GET请求,这可能直接导致target拒绝服务。
最常见的针对应用层DDOS攻击的防御措施,是在应用中针对每个“客户端”做一个请求频率的限制。一个很简单的思路是:通过IP地质与Cookie定位一个客户端,如果客户端的请求在一定时间内过于频繁,则对之后来自该客户端的所有请求都重定向到一个出错页面。从架构上看,这个防御代码需要放在业务逻辑之前,才能起到保护后端应用的目的,可以看作是一个“基层”的安全模块。300页有代码。
但是这个防御方案并不完美,因为用户的IP地址可能会发生改变,而Cookie又有可能会被清空,如果IP地址和Cookie同时都发生了变化,那么就无法再定位到同一个客户端了。
如何让IP地址发生变化呢?使用“代理服务器”是一个常见做法。在实际的攻击中,大量使用代理服务器或傀儡机来隐藏攻击者的真实IP地址,已经成为一种成熟的攻击模式。代理猎手和AccessDriver都是常用的工具。
应用层DDOS的防御主要可以从以下几个方面着手:
1、应用代码要做好性能优化。合理地使用memcache就是一个很好的优化方案,将数据库的压力尽可能转移到内存中。此外还需要及时地释放资源,比如及时关闭数据库连接,减少空连接等消耗。
2、其次、在网络架构上做好优化。善于利用负载均衡分流,避免用户流量集中在单台服务器上。同时可以充分利用好CDN和镜像站点地分流作用,缓解主站的压力。
3、最重要的一点是要实现一些对抗手段,比如限制每个IP地址地请求频率。
验证码的那些事儿
验证码英文简称:CAPTCHA(Completely Automated Public Turing Test to Tell Computers and Humans Apart) 全自动区分计算机和人类的图灵测试。
有验证码,就会有验证码破解技术。除了直接利用图像相关算法识别验证码外,还可以利用Web实现上可能存在的漏洞破解验证码。因为验证码的验证过程,是比对用户提交的明文和服务器端Session里保存的验证码明文是否一致。所以曾经有验证码系统出现过这样的漏洞:因为验证码消耗掉后SessionID未更新,导致使用原有的SessionID可以一直重复提交同一个验证码:
POST /vuln_script.php HTTP/1.0 Cookie:PHPSESSION=329.....; Content-Length:49 Connection:close; name=bob&mail=bob@fish.com&captcha=the_plaintext
形成这个问题的伪代码类似于:
if form_submitted and captcha_stored!="" and captcha_sent=captcha_stored then process_form(); endif:
修改后代码应为:
if form_submitted and captcha_stored!="" and captcha_sent=captcha_stored then captcha_stored=""; process_form(); endif:
还有的验证码的实现方式,是提前将所有的验证码图片生成好,以哈希过的字符串作为验证码图片的文件名。在使用验证码时,则直接从图片服务器返回已经生成好的验证码,这种设计原本的思想是为了提高性能。但这种一一对应的验证码文件名存在一个缺陷:攻击者可以实现采用枚举的方式,比那里所有的验证码图片。并建立验证码到明文之间的一一对应关系,从而形成一张“彩虹表”,这也会导致验证码形同虚设。修补的方式是验证码的文件名需要随机化,满足“不可预测性”原则。
防御应用层DDOS:
验证码不是万能的,而且不利于用户体验。其实验证码的核心思想是识别人与机器,那么人机识别除了靠验证码还有没有其他方面呢?
在一般情况下,服务端应用可以通过判断HTTP头中的User-Agent字段来识别客户端,但是其实这个字段是可以被客户端篡改的,所以不能信任。
一种比较可靠的方法是让客户端解析一段JavaScript,并给出正确的运行结果。因为大部分的自动化脚本都是直接构造HTTP包完成的,并非在一个浏览器环境中发起的请求。因此一段需要计算的JavaScript,可以判断出客户端到底是不是浏览器。类似的,发送一个flash让客户端解析,也可以起到同样的作用。但需要注意的是,这种方法并不会是万能的,有的自动化脚本是内嵌在浏览器中的“内挂”,就无法检测出来了。
除了人机识别,还可以再Web Server这一层做些防御,其好处是请求尚未到达后端的应用程序里,因此可以起到一个保护的作用。比如:
在Apache地配置文件中,有一些参数可以缓解DDOS攻击。比如调小Timeout、KeepAliveTimeout值,增加MaxClients值,但可能会影响正常应用。
Apache提供的模块接口为我们扩展Apache、设计防御措施提供了可能。目前已经有一些开源的Module全部或部分实现了针对应用层DDOS攻击地保护。“mod_qos”是Apache的一个Module,它可以帮助缓解应用层DDOS攻击。比如mod_qos的下面这些配置就非常有价值:
#minimum request rate (bytes/sec at request reading): QS_SrvRequestRate 120 #limits the connections for this viryual host: QS_SrvMaxConn 80 #allows keep-alive support till the server reaches 600 connections: QS_SrvMaxConnClose 600 #aloows max 50 connections from a single ip address QS_SrvMaxConnPerIP 50 #disables connection restrictions for certain clients: QS_SrvMaxConnExcludeIP 172.18.3.32 QS_SrvMaxConnExcludeIP 192.168.10.
我们可以看到mod_qos主要还是限制单个IP地址地访问频率,对于单个IP地址或者较少IP地址的情况下比较有用。但是对于之前提到的代理服务器或者傀儡机的攻击方法就用出不大了。(除了mod_qos,还有mod_evasive也有类似的效果)。
那么如果发起攻击的IP地址较多该怎么解决?
因为发起应用层DDOS攻击的IPdizhi都是真实的,而攻击者地IP地址也不可能无限增长。假设攻击者有1000个IP地址发起攻击,如果请求了10000次,则平均每个IP地址请求同一页面达到10次,攻击者如果持续下去,单个IP地址地请求也将变多,但无论如何,都是在这1000个IP地址地范围内做轮询。为此Yahoo实现了一套算法,根据IP地址和Cookie等信息,可以计算客户端的请求频率并进行拦截。Yahoo设计的这套系统也是为Web Server开发的一个模块,但在整体结构上会有一台master服务器集中计算所有IP地址的请求频率,并同步策略到每台WebServer上。
资源耗尽攻击:
除了CC攻击外,攻击者还可能利用一些Web Server的漏洞或设计缺陷,直接造成拒绝服务。
比如Slowloris攻击:其原理是以极低的速度往服务器发送HTTP请求。由于Web Server对于并发的连接数都有一定的上限,因此若是恶意地占用住这些连接不释放,那么Web Server的所有连接都被恶意连接占用,从而无法接受新的请求,导致拒绝服务。
为了保持住这个连接,可以构造一个不完整地HTTP请求:
GET / HTTP/1.1\r\n Host:host\r\n User-Agent:Mozilla/4.0 ......\r\n Content-Length:42\r\n
之前有提到过在正常地HTTP包头中是以两个CLRF表示HTTP Headers部分结束的:
Content-Length:42\r\n\r\n
所以如果Web Server只收到一个\r\n,因此将认为HTTP Headers部分没有结束,并保持此连接不释放,继续等待完整的请求。此时客户端再发送任意HTTP头,保持住连接即可:
X-a:b\r\n
当如此构造多个连接后,服务器的连接数很快就会达到上限。306页有POC可以看一看。
在Slowloris案例中,“有限”的资源是Web Server的连接数。这是一个有上限的值,比如在Apache中这个值由MaxClients定义。这玩意好像至今还有效。
与Slowloris相似的还有HTTP POST DOS攻击,其原理是在发送HTTP POST包时,指定一个非常大的Content-Length值,然后以很低的速度发包,比如10~100s发一个字节,保持住这个连接不断开。这样当客户端连接数多了以后,占用住了Web Server的所有可用连接,从而导致DOS。309页POC可以看看。
其实这种攻击的本质也是针对Apache的MaxClients限制的。要解决此类问题,可以使用Web应用防火墙,或者一个定制的Web Server安全模块。其实凡是资源有“限制”的地方,都可能发生资源滥用,从而导致拒绝服务。也就是一种“资源耗尽攻击”。
Cookie也能造成一种拒绝服务:Server Limit DOS。Web Server对HTTP包头都有长度限制,以Apache举例,默认是8192字节。也就是说,Apache所能接受的最大HTTP包头大小为8192字节(这里指的是Request Header,如果是Request Body,则默认的大小限制是2GB)。如果客户端发送的HTTP包头超过这个大小,服务器就会返回一个4xx错误。
那么假如攻击者通过XSS攻击,恶意地往客户端写入了一个超长的Cookie,则该客户端在清空Cookie之前,将无法再访问该Cookie所在域的任何页面。这是因为Cookie也是放在HTTP包头里发送的,而Web Server默认会认为这是一个超长的非正常请求,从而导致“客户端”的拒绝服务。
要解决此问题,需要调整Apache配置参数LimitRequestFieldSize,这个参数设置为0时,对HTTP包头的大小没有限制。
所以其实“拒绝服务攻击”的本质实际上就是一种“资源耗尽攻击”,因此在设计系统时,应尽量避免出现“有限资源”被恶意滥用的情况。
一个正则引发的血案:ReDOS
当正则表达式写的不好时,就有可能被恶意输入利用,消耗大量资源,从而造成DOS。这种攻击被称为ReDOS。ReDOS是一种代码实现上的缺陷。正则表达式是基于NFA(Nondeterministic Finite Automaton)的,它是一个状态机,每个状态和输入符号都可能有许多不同的下一个状态。正则解析引擎将遍历所有可能的路径直到最后。由于每个状态都有若干个“下一个状态”,因此决策算法将逐个尝试每个“下一个状态”,直到找到一个匹配的。
比如对于正则表达式^(a+)+$,当输入只有4个“a”时:aaaaX,它只有16条可能的路径,引擎很快就能遍历完。但是当输入以下字符串时:aaaaaaaaaaaaaaaaX,就变成了65536条可能的路径;此后每增加一个“a”,路径的数量就会翻倍,这极大地增加了正则引擎在解析数据时的消耗。在314页和315页可以看到一些存在ReDOS的正则表达式已经一些测试用例。
总之,应用层拒绝服务攻击本质也是对有限资源的无限滥用所造成的。解决这个问题的核心思路就是限制每个不可信任的资源使用者的配额。
参考书籍:《白帽子讲Web安全》
浙公网安备 33010602011771号