2015年5月

防御CC攻击

什么是CC攻击

CC攻击,英文Challenge Collapsar,译为“挑战黑洞”,是一种以服务系统性能薄弱环节为目标的分布式拒绝服务(DDoS)攻击。传统的DDoS攻击一般利用受害者服务器底层网络技术的缺陷发动攻击,攻击者流量消耗比较小。随着技术的进步,已经基本实现有效防御。CC攻击则和传统的DDoS攻击方式不同,它针对的是业务系统应用层的薄弱环节,攻击者需要消耗较多的网络带宽才能发动,但由于根据业务特点发动攻击,目前没有通用的有效的防御手段,所以攻击成功率比较高。

服务器业务中的性能薄弱环节,未必是软件实施的缺陷。比如常见的CMS网站,首页在Cache等技术下,承载上万RPS(每秒请求)不会有任何问题,但是CMS的搜索功能,也许只能承载几百RPS。考虑到平时网站用户量不大,实际首页压力只有几百RPS,而搜索只有几RPS的实际情况,网站完全能够正常运行。此时如果黑客断定搜索是性能薄弱环节,针对搜索功能发起CC攻击,通过遍布全国的几百个代理服务器,向网站发送搜索请求,很快网站就会不堪重负,被迫下线了。

网站中需要处理复杂事务的接口,一般都有可能会成为CC攻击的目标,包括搜索、聊天、短信发送、验证码生成等等。

侦测CC攻击

防御CC攻击较之防御传统的DDoS攻击,一大难点是不易侦测攻击的存在。从单个请求来看,CC攻击者和普通用户几乎完全相同,一般只能从宏观层面,比如一段时间的网站流量统计中才能发现自己被攻击了。找到方法甄别来访者是普通用户还是攻击者,是解决CC攻击的关键。

检查代理服务器

一般用户都是直接访问互联网,而大多数CC攻击采用代理服务器发起,检测来访用户是否使用代理服务器是一个判别的方法。公共HTTP代理一般会在请求中加入X-Forwarded-ForVia请求标头,可以以此为据侦测用户是否使用了代理服务器。

不过近年出现了不少高匿名的代理服务器,完全不透传任何信息,这个方法就无能为力了。

检查IP的并发连接数

根据HTTP协议的规定,用户代理对每个域应该允许最多2个并发连接,这一标准在06年以来有所提高,每个域允许6个并发连接。正常情况下,每个连接到服务器的IP处于ESTABLISHED状态的连接数应该不超过n*6个,n是该服务器同时对外服务的域名数量。如果来自同一个IP存在大量的并发连接,则有可能是攻击者。我们可以使用netstat命令实时地查看TCP连接的情况,配合脚本可以统计出当前各个客户端IP的并发连接数。

当然这个方法并不绝对,有的网吧几十台机器共用一个IP作为出口,那么这个IP的并发连接数量就会大得多。还有一些小的ISP可能只有少量的公网IP,它们在建设小区宽带的时候甚至会让一幢楼的用户共用一个公网IP。

高频请求检测

发动CC攻击一般都用脚本让机器自动发起请求,如果发现某个IP以非常高的频率发起HTTP请求,这个IP就有可能是攻击者。收集和处理高频请求的数据,需要占用一些服务器的CPU和内存资源。判定是否高频,一般以正常用户的页面平均停留时间乘以一个系数来计算需要根据实际情况不断调整。

这一方法和前面的并发连接数法有着相同的问题,如果用户存在共用出口公网IP的情况,就可能误判。另一个问题是,如果攻击者采用游击战术,每个代理只发起一波请求,然后弃之不用,这一方法就不会有实际效果,因为发现攻击者之后,攻击者已经换地方了。

重复IP检测

对于低留存率的网站,可以计算出连续若干天的来访者IP交集,去掉搜索引擎的和白名单上的IP之后,可以认为这些IP是攻击者。

这一方法的局限性在于需要攻击持续一段时间,并且攻击者只使用少数固定的代理服务器。如果攻击者打游击,这一方法也不会有效。另外,高留存率的网站可能不适用此方法,因为本来用户就会来了又来。

浏览器特性检测

有的CC攻击发起者只是为了对服务器的复杂逻辑发起请求,让服务器负担加大。有的攻击者会放弃使用全功能的浏览器,而改用一般的Python脚本发起。如果攻击者使用的不是完整的浏览器,我们就可以下发JS脚本运行,并用Feature Hack的方法确认用户的浏览器和它在UA中声称的自己的身份是否一致,进而推断是否为攻击者。

反向扫描来访IP的端口

一般的家用宽带用户不会开启大量非常用端口,反向扫描来访者IP开启的端口情况,如果发现对方在Listen许多非常用端口,则说明对方很可能是一台代理服务器或者被黑客操控的肉机。

反向扫描的方法耗时比较长,对一波流的攻击者效果不显著。

业务保护

如果我们无法有效辨别一个来访请求是CC攻击还是普通用户,也可以从业务角度入手,保护复杂业务正常进行。CC攻击是由机器自动完成,如果被攻击的功能能够识别人类用户,虽然无法降低带宽成本,但是可以有效减少业务损失。

加验证码

在关键功能上加验证码是一种直接的方法,可以参考12306的登录功能,使用验证码保护登录接口不会被机器自动提交攻击。

对抗非浏览器

如果攻击者使用的客户端不是浏览器,无法运行网页中的JavaScript程序,可以在页面中加入JavaScript验证程序,让一般的网页抓取工具失效。Wordpress的反垃圾留言就有类似的实现方法,它使用随机生成的JS和JS注释拼装出一个Token和留言一起提交,在服务器端检查Token是否合法,因为Token是混在大量JS脚本中的,一般的正则工具很难把Token分析出来,从而实现了无需输入验证码的简单人机区分。

鼠标行为探测

如果攻击者使用的是全功能浏览器,那么前面的方法不会有效,但依然有办法侦测对方是人类还是机器。鼠标移动轨迹就是一个方法。人们访问网页的时候,鼠标一般不会以固定的方式移动。对于那些不希望验证码降低用户体验的网页,可以用鼠标移动监测的方式判断是否人类用户。这一方法可以和滚动条状态等方法一起使用,对于平板电脑用户,还可以加入触屏行为监测。

当然,如果被攻击者发现了,攻击者也能写出模拟鼠标随机移动的程序,此时这一方法的效果就会打折扣了。

延迟业务自动开启

我们被攻击的地方是浏览器自动弹出的客服聊天窗口,考虑到自动机攻击时页面开启时间比较短,而正常用户页面开启时间比较长,可以延迟自动聊天窗口的弹出,绕过攻击。

按地区过滤非主流用户

这个方法是结合具体业务而设计的。由于被攻击的业务集中在临近的几个省,可以利用IP地理位置库设置规则,只放行主要业务地区的线上来访请求,其它地区则转向线下电话咨询。

SQLite3.7.11以上版本才支持一条INSERT语句插入多行数据

最近被SQLite坑了:一个在Ubuntu14.04的节点上运行正常的PHP程序迁移到CentOS 6.4的服务器之后就一直报SQL Syntax Error。出错的是一条批量插入数据的INSERT语句:

INSERT INTO table (col1, col2) VALUES
  ('row1col1', 'row1col2'),
  ('row2col1', 'row2col2');

报错信息也非常简单:

PHP Warning:  SQLite3::exec(): near ",": syntax error in /tmp/test.php on line 32

这里不得不吐槽一下SQLite的报错信息,你这是在逗我呢吧,我语句里逗号那么多,谁知道你说的哪一个!!相比之下MySQL的报错就友善得多,会把出错的位置之后的SQL一起打出来,一眼就能定位到出错的地方。

言归正传,原来SQLite还真是够Lite的,在3.7.11版之前压根儿不支持一条语句多行插入1。Ubuntu14.04上自带的SQLite版本是3.8.2,而CentOS 6.4上自带的仅是3.6.20。而SQLite官网上的文档也是够Lite的2,虽然一个语法图无比清晰明了,但是却完全无法体现出版本兼容性的差异。不求你像MySQL一般为每个版本写一份文档,但也起码在每篇后面加几条Hint吧?

无奈人家就是要Lite,只好自己写Blog做笔记。


  1. http://stackoverflow.com/questions/1609637/is-it-possible-to-insert-multiple-rows-at-a-time-in-an-sqlite-database/1609688#1609688 

  2. http://sqlite.org/lang_insert.html