如果是没有做过攻击防御的小网站遇到DDoS或CC攻击,会导致服务器无法被正常访问,或访问体验慢,甚至直接瘫痪宕机。不过,如果你没有得罪大佬的话,一般攻击时间不会很久,有可能被误伤了,但是如果你网站跟大佬有利益冲突,那么你只能上高防IP或关机保平安了。众所周知,DDoS攻击防御是一个世界性的难题,目前做得最好的当属Cloudflare。幸运的是,Cloudflare提供免费的无限级CC和DDoS攻击防御,所以一灯不是和尚建议各位中小站长提前做好攻击防御工作,以避免被攻击时损失更大。如果我们买不起高防服务器,或者觉得买高防性价比太低,那么我们可以选择Cloudflare的免费CDN,并配合Nginx和iptables配置规则进行低成本防御,但是要损失一些用户体验。虽然,Cloudflare的免费CDN在国外加速效果非常牛逼,但是在天朝境内的话,其CDN加速在部分地区被誉为云减速。
本文目录
1、接入Cloudflare免费无限级防御
如果你的网站服务器被持续攻击,并丝毫没有缓解的征兆的话,我建议尽快接入Cloudflare解析,并开启CDN加速的攻击保护“Under Attack”模式,此时著名的5秒盾回自动开启。如下图所示:
这时候,任何访问你网站的用户都会看到一个5秒验证提示,此种方法对DDoS和CC攻击非常有效。
2、限制攻击频繁地域的IP访问
通常,很多攻击者使用僵尸网络来自于海外,以美国IP居多。如果你的网站主要面对中文用户的话,那么你可以直接限制国外IP对网站的访问;如果你的访客主要面对海外用户的话,那么你直接一直接入Cloudflare的CDN网络就可以了,毕竟Cloudflare的攻击防御能力是首屈一指的,还没有被打死过,而且非中国大陆地区的Cloudflare的免费版CDN加速效果也非常好;如果你的网站同时向全球用户提供访问,那么你只能先屏蔽较多攻击者所在地域的IP,等缓解之后在逐步调试。
另外,CloudFlare的防火墙还提供了Cookie、IP地址、主机名等定制化规则,你可以进行更加详细的攻击防御定制。一般情况下,我们将CloudFlare设置为质询就可以了,当然你也可以选择直接阻止。此时,我们在CloudFlare的防火墙日志中能看到已经阻止的攻击者IP了。如下图所示:
3、仅允许 Cloudflare IP 访问
现在,我们已经阻挡了大部分的攻击者了,但我们还需要对服务器做进一步的设置:仅允许CloudFlare IP访问。有些服务器在开启CDN后获取到的IP会是CDN的IP,并不是真实用户的IP地址,此时我们需要对Nginx或者Apache做进一步的调整。
(1)Nginx仅允许CloudFlare IP
有的时候我们需要借助Cloudflare 的安全防护功能来防止CC或者DDoS攻击,即仅允许Cloudflare CDN的IP访问我们的访问。
Nginx直接拒绝和允许IP访问代码示例如下:
location / { deny 192.168.1.1; allow 192.168.1.0/24; allow 10.1.1.0/16; allow 2001:0db8::/32; #Railgun IP deny all; }
如果我们仅允许Cloudflare CDN的IP访问网站,我们可以直接在nginx配置中将Cloudflare CDN的IP添加到允许的范围内。
#直接加入 # https://www.cloudflare.com/ips # IPv4 allow 103.21.244.0/22; allow 103.22.200.0/22; allow 103.31.4.0/22; allow 104.16.0.0/12; allow 108.162.192.0/18; allow 131.0.72.0/22; allow 141.101.64.0/18; allow 162.158.0.0/15; allow 172.64.0.0/13; allow 173.245.48.0/20; allow 188.114.96.0/20; allow 190.93.240.0/20; allow 197.234.240.0/22; allow 198.41.128.0/17; # IPv6 allow 2400:cb00::/32; allow 2405:8100::/32; allow 2405:b500::/32; allow 2606:4700::/32; allow 2803:f800::/32; allow 2c0f:f248::/32; allow 2a06:98c0::/29;
自动更新Cloudflare CDN的IP。手动添加Cloudflare CDN的IP到Nginx配置当中简单方便,但是一旦Cloudflare CDN的IP有变化时还得自己手动处理,我们可以创建一个脚本,定时去更新Cloudflare CDN的IP,自动添加到Nginx配置中,代码如下:
touch /usr/local/nginx/conf/allow_ip.conf #修改网站nginx配置,加入以下代码: include /usr/local/nginx/conf/allow_ip.conf; vim /data/script/allow_cf_ip.sh #!/bin/bash echo "#Cloudflare" > /usr/local/nginx/conf/allow_ip.conf; for i in `curl https://www.cloudflare.com/ips-v4`; do echo "allow $i;" >> /usr/local/nginx/conf/allow_ip.conf; done for i in `curl https://www.cloudflare.com/ips-v6`; do echo "allow $i;" >> /usr/local/nginx/conf/allow_ip.conf; done #添加定时任务 0 5 * * 1 /bin/bash /data/script/allow_cf_ip.sh
(2)iptables防火墙仅允许CloudFlare IP访问
添加cloudflare ips-v4到 iptables 白名单的命令
for i in `curl https://www.cloudflare.com/ips-v4`; do iptables -I INPUT -p tcp -m multiport --dports http,https -s $i -j ACCEPT; done
添加cloudflare ips-v6 iptables 白名单的命令
for i in `curl https://www.cloudflare.com/ips-v6`; do ip6tables -I INPUT -p tcp -m multiport --dports http,https -s $i -j ACCEPT; done
丢弃白名单以外的 ipv4 80,443 tcp 包
iptables -A INPUT -p tcp -m multiport --dports http,https -j DROP
丢弃白名单以外的 ipv6 80,443 tcp 包
ip6tables -A INPUT -p tcp -m multiport --dports http,https -j DROP
(3)Cloudflare获取访客用户的真实IP
有些服务器在开启CDN后获取到的IP会是CDN的IP,并不是真实用户的IP地址,此时我们需要对Nginx或者Apache做进一步的调整。
4、分析日志自动启用防火墙
(1)一键屏蔽指定地域IP访问
一键屏蔽指定国家所有IP访问的项目官网:https://github.com/iiiiiii1/Block-IPs-from-countries
原理是下载指定国家的IP段,然后将IP段添加到iptables规则当中,直接执行以下命令:
wget https://raw.githubusercontent.com/iiiiiii1/Block-IPs-from-countries/master/block-ips.sh chmod +x block-ips.sh ./block-ips.sh
然后会提醒你,选择封禁IP,还是解封IP。如下图所示:
选择封禁IP后,会让你输入国家代码,请到这里查看:http://www.ipdeny.com/ipblocks,例如美国就是输入us,回车后完成对整个美国IP封禁。如下图所示:
(2)使用脚本定时检测日志
我们能否通过分析日志来确认恶意的访问请求呢?我们可以监控服务器日志,将请求出错的访问者 IP 放入一个 List 特别观察,在一段时间内如果没有太多的出错,我们就将其从列表中移除,否则,错误太多达到警戒值就调用 iptable 将其禁封。脚本代码如下:
#!/usr/bin/perl use strict; use warnings; ## 本脚本将会监控 Web 服务器的 log 记录,(例如 Apache 或者 Nginx) ## 并统计同一个 IP 所引发的 HTTP 错误数目。该数值达到用户配置的数量, ## 则使用防火墙对该 IP 进行屏蔽,拒绝其访问。 ## log 文件路径 my $log = "/var/log/nginx/access.log"; ## 一个 IP 触发了多少次错误,我们就将其屏蔽? my $errors_block = 10; ## 过期时间,超过多少秒没有再见到该 IP 则将其从观察列表中移除? my $expire_time = 7200; ## 将 IP 从观察列表中移除时,清理多少个错误日志行数? my $cleanup_time = 10; ## 调试模式 on=1 off=0 my $debug_mode = 1; ## 声明一些内部变量 my ( $ip, $errors, $time, $newtime, $newerrors ); my $trigger_count=1; my %abusive_ips = (); ## 打开日志文件。使用系统的 tail 命令,有效轮询 open(LOG,"tail --follow=$log |") || die "Failed!\n"; ## For Linux (Ubuntu) systems # open(LOG,"tail -f $log |") || die "Failed!\n"; ## For OpenBSD, FreeBSD or Linux systems while(<LOG>) { ## 定义错误代码。这里使用了正则表达式匹配,你可以自行添加一些, ## 例如无端访问 .vbs 后缀文件请求,列入屏蔽条件 if ($_ =~ m/( 401 | 402 | 403 | 404 | 405 | 406 | 407 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 444 | 500 | 501 | 502 | 503 | 504 | 505 )/) { ## 自定义: 白名单 IP。 不论这些 IP 做了什么,均不屏蔽。 ## Google 百度等爬虫 IP 段 加入到白名单中。 ## 为了方便程序员开发测试,内部子网 192.168/16 也不屏蔽。 if ($_ !~ m/(^66\.249\.\d{1,3}\.\d{1,3}|^216\.239\.\d{1,3}\.\d{1,3}|^192\.168\.\d{1,3}\.\d{1,3}|^116\.255\.\d{1,3}\.\d{1,3}|^220\.181\.\d{1,3}\.\d{1,3}|^123\.125\.\d{1,3}\.\d{1,3}|^203\.208\.\d{1,3}\.\d{1,3}|^181\.108\.\d{1,3}\.\d{1,3}|^180\.76\.\d{1,3}\.\d{1,3}|^183\.60\.\d{1,3}\.\d{1,3}|^210\.72\.225\.\d{1,3}|^159\.226\.50\.\d{1,3}|^61\.135\.168\.\d{1,3})/) { ## 从日志行中解析出 IP $time = time(); $ip = (split ' ')[0]; ## 若 IP 之前从未出现过,我们需要初始化,以避免出现警告消息 $abusive_ips{ $ip }{ 'errors' } = 0 if not defined $abusive_ips{ $ip }{ 'errors' }; ## 给这个 IP 增加出错计数,更新时间戳 $abusive_ips{ $ip }{ 'errors' } = $abusive_ips{ $ip }->{ 'errors' } + 1; $abusive_ips{ $ip }{ 'time' } = $time; ## DEBUG: 输出详细信息 if ( $debug_mode == 1 ) { $newerrors = $abusive_ips{ $ip }->{ 'errors' }; $newtime = $abusive_ips{ $ip }->{ 'time' }; print "unix_time: $newtime, errors: $newerrors, ip: $ip, cleanup_time: $trigger_count\n"; } ## 如果该 IP 已经触发 $errors_block 出错数量,调用 system() 函数屏蔽之 if ($abusive_ips{ $ip }->{ 'errors' } >= $errors_block ) { ## DEBUG: 输出详细信息 if ( $debug_mode == 1 ) { print "ABUSIVE IP! unix_time: $newtime, errors: $newerrors, ip: $ip, cleanup_time: $trigger_count\n"; } ## 自定义: 这里是屏蔽 IP 的 system() 系统调用 ## 你可以对这个 IP 添加更多的执行操作。例如,我们使用 logger 打印记录到 /var/log/messages ## 注释掉的是 OpenBSD 系统 Pf 防火墙 system("logger '$ip blocked by calomel abuse detection'; iptables -I INPUT -s $ip -j DROP"); # system("logger '$ip blocked by calomel abuse detection'; pfctl -t BLOCKTEMP -T add $ip"); ## 当 IP 已经被屏蔽,它就没必要继续留在观察列表中了 delete($abusive_ips{ $ip }); } ## 为后面的清理函数增加触发计数 $trigger_count++; ## 清理函数:当触发计数达到 $cleanup_time 我们将所有已经过期的条目从 $abusive_ips 列表中删除 if ($trigger_count >= $cleanup_time) { my $time_current = time(); ## DEBUG: 输出详细信息 if ( $debug_mode == 1 ) { print " Clean up... pre-size of hash: " . keys( %abusive_ips ) . ".\n"; } ## 清理我们已经很久没再见到的 IP while (($ip, $time) = each(%abusive_ips)){ ## DEBUG: 输出详细信息 if ( $debug_mode == 1 ) { my $total_time = $time_current - $abusive_ips{ $ip }->{ 'time' }; print " ip: $ip, seconds_last_seen: $total_time, errors: $newerrors\n"; } ## 如果 IP 未出现的时间已经超过我们设定的过期时间,则将其从列表中移除 if ( ($time_current - $abusive_ips{ $ip }->{ 'time' } ) >= $expire_time) { delete($abusive_ips{ $ip }); } } ## DEBUG: 输出详细信息 if ( $debug_mode == 1 ) { print " Clean up.... post-size of hash: " . keys( %abusive_ips ) . ".\n"; } ## 重置清理触发计数 $trigger_count = 1; } } } } #### EOF ####
保存以上内容到 web_server_abuse_detection.pl,增加可执行权限。命令如下:
chmod +x web_server_abuse_detection.pl
设置 my $debug_mode = 0;
脚本即会静默运行。要让它在后台运行,不占用终端,则在命令后加一个 & 符号
./web_server_abuse_detection.pl &
5、使用Cloudflare对DDoS和CC攻击防御总结
从使用体验来看,开启Cloudflare的5秒盾是解决中小型网站防御DDoS和CC攻击最有效且性价比最高的方法,而且可以利用Cloudflare自带的防火墙可以对攻击者的IP进行区别对待,从而最大限度地提高防御效果。另外,如果你的网站架设在国内运营商的服务器上,那么很遗憾,当你开启Cloudflare的时候可能已经被打进黑洞了。尤其,像阿里云、腾讯云这些大厂的云主机,一旦遭遇到攻击且超过5G防御峰值直接进入空路由的黑洞状态,10小时以后再把你放出来。遇到这种情况,我们应该怎么办?一灯不是和尚建议你使用国内大厂的虚拟主机服务,共享资源的那种,防御峰值较高,而且一般不会直接给你黑洞,即使宕机还可以重启。当然,我还是推荐你使用国外的VPS或独立服务器提供商,比如美国的搬瓦工(BandwagonHOST)、DMIT或 iON Cloud 等,被打进黑洞的状态较短(一般不超过1小时),且提供高防服务器。
本文由一灯不是和尚于2021年9月16日更新;如果您有什么意见或建议,请在文章下面评论区留言反馈。