代码狗博客建站三年了,从未重视过网站安全问题,毕竟个人小博客没必要多在乎安全问题,然而今天有位未知的朋友用实际行动告诉我一定要重视网站安全,不然损失的就不只是流量!特别是加了CDN的网站,那流量跑得飞起,然后你会为巨额流量买单。下面是今天我被CC攻击的全过程,以及我对此次攻击做出的补救措施,第一次搞网站防御若有错误欢迎指正。
第一次CC攻击
早上10点15分,突然收到腾讯云发来的CDN流量包即将消耗殆尽的邮件及短信通知,当时我就懵了,按照我一天500M的流量来算,不应该不够呀。然后瞬间意识到被攻击了 ,登陆腾讯云后台看到今日消耗流量40G。心都凉了半截,然后我快速设置了下腾讯CDN的请求次数控制,之前没有限制。限制每IP每秒仅可请求20次,然后设置CDN带宽阈值,超过自动回源。
最坑爹的是腾讯的CDN日志有2个小时左右的延迟,我根本没办法使用封禁IP的方式来解决这个问题。快12点我才看到腾讯CDN的日志信息,发现了一大堆下面形式的请求记录。
攻击者模拟了百度的UA,使用GET方法大量请求我的首页,使用的ip据查大部分是腾讯云的,而且ip杂乱,总共有高达600+ip,封禁ip的方法行不通了。
第二次CC攻击
下午在一个WordPress交流群中交流今天上午被CC攻击的事,刚说完,立马就受到了第二波攻击。这一次由于我设置了超过请求频率就会返回404页面,所以遭受的损失少了许多。找客服交流了许久,没有任何办法防御CC攻击,CDN流量损失也挽回不了。我开始在自己服务器上使用ngx_lua配置WAF防火墙,用来防御一部分CC攻击。既然CDN不靠谱,那么我还是靠自己吧,关键是CDN的流量跑起来太心痛了!
使用ngx_lua配置WAF防火墙为Nginx开启防御功能有很多种方式,最简单的就是使用oneinstack一键安装包安装服务器环境,oneinstack在2019年1月1日更新版本中新增了ngx_lua功能,可以自动安装配置,小白必备。
另一种就比较麻烦,需要自己手动下载源码,编译安装,然后配置Nginx支持,设置防火墙规则等等,一两句话说不完,这里就不多说了,需要的可以自行百度。
第三次CC攻击
下午3点05分,第三次CC攻击来临,这位老兄今天是耗上我了,不打死我的小博客不死心呀。CDN跑了10多G流量后成功回源,然后我的服务器防火墙也记录下了被拦截的CC攻击请求,如下:
图中我们可以看到拦截了一批CC攻击,但是仍然有一批透过了防御。
使用lua脚本+memcached动态黑名单防御
既然lua自己的CC防御逻辑拦不住,那我们就写一段拦截脚本,把一段时间内访问次数过多的用户ip给封禁处理。这里就需要使用memcached内存缓存了,当然你也可以使用redis缓存,也可以直接写到文件中,内存的响应比较快,我采取了memcached内存缓存策略。
在lua脚本文件中写入如下代码:
ip_bind_time = 300 --封禁IP时间 ip_time_out = 1 --指定ip访问频率时间段 connect_count = 60 --指定ip访问频率计数最大值 local memcached = require "memcached" local memc, err = memcached:new() if not memc then ngx.say("failed to instantiate memc: ", err) return end local ok, err = memc:connect("127.0.0.1", 11211) if not ok then ngx.say("failed to connect: ", err) return end is_bind , err = memc:get("bind_"..ngx.var.remote_addr) if is_bind == "1" then memc:close() ngx.exit(403); return; end start_time , err = memc:get("time_"..ngx.var.remote_addr) ip_count , err = memc:get("count_"..ngx.var.remote_addr) --如果ip记录时间大于指定时间间隔或者记录时间或者不存在ip时间key则重置时间key和计数key --如果ip时间key小于时间间隔,则ip计数+1,且如果ip计数大于ip频率计数,则设置ip的封禁key为1 --同时设置封禁key的过期时间为封禁ip的时间 if start_time == nil or os.time() - start_time > ip_time_out then res , err = memc:set("time_"..ngx.var.remote_addr , os.time()) res , err = memc:set("count_"..ngx.var.remote_addr , 1) else ip_count = ip_count + 1 res , err = memc:set("count_"..ngx.var.remote_addr,ip_count) if ip_count >= connect_count then res , err = memc:set("bind_"..ngx.var.remote_addr,1,ip_bind_time) end end
重载后手动测试,连续刷新首页4次,成功返回403错误。然后使用Linux机器利用ab工具进行压力测试,并发1000,访问后发现仍然会对正常用户产生影响,网页打不开了。
貌似这个脚本会对封禁的黑名单发起的请求进行处理,1W个黑名单请求就会处理1W次,所以仍然会阻塞正常用户的访问,不过如果CC攻击是持续性的,那么这段脚本还是有点用处的,除了第一波会影响正常访问外,后面就会好很多。建议将黑名单加入服务器防火墙,不经过nginx,这样就不会出现卡顿情况,否则即使进入nginx黑名单,nginx也会处理请求,仍然会被消耗资源。
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/242393.html