网站被 CC 攻击的一次经历

一直感觉网站被攻击那都是新闻里的事情,可就是上个月,连续五六天收到服务器负载过高的警告邮件,看了一下进程记录发现是 PHP 占据了80%~90%的内存并且 System Load 都是100%,平均负载竟然是 “21.28 13.57 5.48″,这可不是开玩笑的。我又看了 Nginx 的访问日志,发现连续几天几乎在同一时刻有大量的 IP 疯狂请求网站,虽然比不上那些新闻中的攻击量,但也足够让我的小服务器挂掉。这时我意识到我网站被人盯上了,而且是 CC 攻击。于是乎,我便到处寻找防御的方法,这里呢,就记录一下整个过程,希望能帮助到那些跟我有同样遭遇的人。

开端

从十一月六号到十六号,每天的十一点到十二点之间,我收到了服务器高负载的警告邮件,一开始我没在意,可是连着几天后我觉得事情有点不对劲,就到服务器上看了一眼,发现是 PHP 几乎占据了全部的内存,于是我减少了 PHP 的最大进程数,这样做之后似乎有效,之后的几天都没收到警告邮件。在十一月底的时候我看了一眼 PHP 的日志,想找出 PHP 究竟出了什么问题,发现每次高负载的时候 PHP 都出现了这样的日志:

紧接着 PHP 的进程数就达到了上限,这时 PHP 日志又出现了这样的提示:

说明此时我的网站响应速度已经是巨慢的了,而且连着好几个这样的日志,于是我就查看 nginx 访问日志,这一看不要紧,发现在那一时刻有大量的 IP 大量访问网站地址,而且还是随机的地址,感觉它一下子请求了我博客中所有的地址,请求的速度大概是一秒十个以上。虽然它攻击的速度没那么大,直接访问静态的页面也不至于让服务器宕机,但是我网站可都是 PHP 动态页面,这大量的请求一下子都交给 PHP 去处理,PHP 肯定要炸了呀。

分析

这时我意识到我网站被人盯上了,可我只是搞了一个小博客玩玩而已,怎么会被人给盯上了呢?当然想这些也没用,还不如好好想想怎么防住这些攻击。在网上搜了一些相关的资料,才知道这种攻击属于 DDoS 的一种,但不是 DDoS,而是 CC 攻击,这两者最大的区别就是 DDoS 是针对 IP 的攻击,而 CC 攻击的是网页,也就是说,CC 攻击中每个请求都是合法的,就像是正常用户访问网页;CC 攻击的来源 IP 都是分散的,也就是通过代理服务器用大量 IP 来发送请求。攻击我的 IP 都是在一个24位的子网中,单靠防火墙是无法有效防止的,因为短时间内同一 IP 请求次数可能并没有达到屏蔽的量。由此可见,CC 攻击最可怕之处在于攻击的 IP 数巨大,单靠硬件防火墙根本无法屏蔽。

攻防之战第一回合

CC 攻击有一个致命点,那就是它请求的都是动态资源,比如 PHP ,因此我就打算从限制 PHP 并发入手,减少 PHP 的并发数目,以防万一我还限制了 nginx 单个 server 的并发数目。对于我这种小博客,限制5个并发就足够了,也不会有那么多人同时访问我的博客。

隔了一两天我去查看 nginx 日志时又发现,攻击并未停止,而且攻击请求的返回状态码竟然是499,这个状态码我可第一次见,查了一下资料得知这个错误码的意思是 “CLIENT CLOSED REQUEST” ,也就是客户端关闭了链接,当然关闭的原因一般就是服务器处理请求处理的太慢,然后客户端就等不及了,直接关闭了请求。像极了我们平时打开一个网页发现等半天还是在转圈圈,于是就关闭了这个网页。既然出现了这个错误码,也就说明最终请求还是交给了 PHP 来处理,PHP 处理过慢,攻击者为了进行下一个攻击就关闭了这个请求,看来限制并发并不能挡住 CC 攻击。

第一回合我败

第二回合

随后我又从防火墙屏蔽 IP 上入手,使用了张戈大佬写的 CCKiller 脚本,试图直接将 IP 用 iptables 来屏蔽掉。为了更好的统计 IP 访问次数,我还修改了脚本,让它只监听某一端口和某一状态的 IP 访问,同时将 IP 的屏蔽频率限制到了每2秒超过3次就屏蔽,虽然有点极端,可是为了挡住 CC 攻击也顾不了那么多了。第二天看了一下 nginx 日志,发现并不起作用由于攻击者足足使用了一个段的 IP 来发送请求,同一个 IP 出现两次的间隔甚至超过了5秒,这也可以理解,毕竟一个段的 IP 都有254个可用 IP,一秒就算十次请求,要用完这254个 IP 也需要25秒,这让我怎么根据 IP 访问频率来屏蔽???算了,这回合我又输了

第三回合

第三回合要从我无意间发现宝塔面板中的 NGINX 管理那里有一个过滤器,叫做 waf,其中的配置中有一个防 CC 攻击的配置说起。我试着启用了防 CC 攻击功能,并设置 CC 攻击触发频率为5次,触发周期为2秒。等到了第二天,发现 waf 的日志中并没有任何防住 CC 攻击的记录,看了一下 nginx 访问日志,还是有被攻击的大量请求。抱着好奇的心态,我看了一下宝塔使用的 waf 源码,发现是用 lua 写的,在源码中防 CC 攻击这一块,发现它是根据 IP + 请求的URL 作为依据来统计频率的,这玩意怎么能防住大量IP+随机URL请求呢?好吧,这一回合又是我输

第四回合

经过上一回合的失败,我决定修改这个 lua waf ,在网上查了查,发现它是基于 Nginx 的 Lua 语言模块:”OpenResty” 开发的一个 nginx lua waf 防火墙,原作者是@loveshell,瞬间感觉这个项目好厉害,更加坚定了修改这个项目的决心。在看了几天源码之后,大概看懂了这个项目的思想,就开始动手改了。我首先改的是防 CC 的判断依据,将它由 IP+URL 改为了 IP+HOST,这样就可以防止随机 URL 请求。上线测试时发现对于某些特定 URL,比如小程序接口,会造成误杀的情况,因为小程序的一个页面会有好几个API 请求,于是又改进了原项目的 URL 白名单功能,修改为 HOST+URL 的白名单功能。修改完成,上线测试。结果嘛,还是失败了,毕竟还是使用 IP 来作为判断依据的。虽然能屏蔽住一些同一IP大量异常请求的攻击,但还是防不住之前的 CC 攻击。无奈,这一回合我又输了

第五回合

到了这一回合,我决定从分析访问日志下手,肉眼看了N行日志之后,发现这些攻击竟然有一个相同点,那就是日志中都出现了”http://www.baidu.com/s?wd=6HJW”,看到这我想攻击可能是从百度索引来的,这才造成这么多的请求URL,也有可能是读取了我的站点地图,然后根据站点地图中的 URL 来作为请求 URL。既然这样那我总不能不让百度来索引我网站或者不用站点地图吧,这样子对搜索引擎太不友好了。这时,我转念一想,这些请求中为什么都要加一个类似”http://www.baidu.com/s?wd=6HJW”这样的来源 URL 呢?查了一下 nginx 的日志,发现这部分是请求头中的 “http_refer” 部分,也就是记录当前请求是从哪个 URL 来的。可能攻击者是让服务器认为这些请求是来自百度搜索,避免被屏蔽吧。

这时我转念一想,我为啥不根据 “http_refer” 来作为统计频率的依据呢?既然所有的攻击请求中都带有同样的 “http_refer”,我完全可以根据它来限制请求呀,这样子不管来源 IP 是啥,我都可以做到匹配了。既然有这个想法,就立刻动手改程序。就在前天,我又一次更新了 nginx lua waf 项目,添加了根据 http_refer 来限制访问的功能,并把它发布到网站中,静静地等待下一波攻击。

等待了一天,周六,没攻击我,今天,我看了一下 waf 日志,发现 http refer CC Deny 有了一条记录!激动的我赶紧查看 nginx 的访问日志,果然在十二点左右来了一波攻击,相同的手段:24位掩码的 IP 段、随机的请求 URL、相同的 http_refer,它终于来了!看了一下它所有的请求,在触发了 http refer cc deny rate 之后,所有请求都变成了 503 返回码,不再是 200 或者 499 了。我查看 PHP 的日志记录,并没有任何异常,服务器负载也没有任何异常!哈哈哈哈,这一回合算我胜了吧

下面附上 waf 和 nginx 的访问日志:

总结

经历了这次被攻击事件,我也学到了很多,也发现运维这个坑真的是深不可测当然了,这次攻防之战也不一定就此结束,也不知道那小子会不会再来呢?不过嘛,魔高一尺道高一丈,想再来那我就再陪你玩一玩,我还能借此机会不断地提高自己呢

对了,上面说的更新后的 nginx lua waf 项目已经被我放到了 ehaut 项目组下,链接在此:ehaut/ngx_lua_waf,我自己 fork 了一份用来开发,测试好后就会 push 到 ehaut 下的。之后呢,我会具体写篇文章说明这个项目的,感兴趣的朋友可以关注一下本站。

温馨提示

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181210G0G65E00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券