使用ngx_lua配置WAF防火墙为网站防御CC攻击

代码狗博客建站三年了,从未重视过网站安全问题,毕竟个人小博客没必要多在乎安全问题,然而今天有位未知的朋友用实际行动告诉我一定要重视网站安全,不然损失的就不只是流量!特别是加了CDN的网站,那流量跑得飞起,然后你会为巨额流量买单。下面是今天我被CC攻击的全过程,以及我对此次攻击做出的补救措施,第一次搞网站防御若有错误欢迎指正。

第一次CC攻击

早上10点15分,突然收到腾讯云发来的CDN流量包即将消耗殆尽的邮件及短信通知,当时我就懵了,按照我一天500M的流量来算,不应该不够呀。然后瞬间意识到被攻击了 ,登陆腾讯云后台看到今日消耗流量40G。心都凉了半截,然后我快速设置了下腾讯CDN的请求次数控制,之前没有限制。限制每IP每秒仅可请求20次,然后设置CDN带宽阈值,超过自动回源。

最坑爹的是腾讯的CDN日志有2个小时左右的延迟,我根本没办法使用封禁IP的方式来解决这个问题。快12点我才看到腾讯CDN的日志信息,发现了一大堆下面形式的请求记录。

使用ngx_lua配置WAF防火墙为网站防御CC攻击

使用ngx_lua配置WAF防火墙为网站防御CC攻击

攻击者模拟了百度的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攻击请求,如下:

使用ngx_lua配置WAF防火墙为网站防御CC攻击

使用ngx_lua配置WAF防火墙为网站防御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

(0)
上一篇 2022年4月7日 17:43
下一篇 2022年4月7日 17:44

相关推荐

发表回复

登录后才能评论