快捷搜索:   nginx

Nginx CDN加速环境下 封禁真实访客IP 限速防CC攻击方法 宝塔 反代环境也可用

现在CDN应用越来越广泛,但是这样也相应的产生一些防御上的问题,比如我们使用CLOUDFLARE的CDN做防御,但是CLOUDFLARE抗DDOS能力是很强,但是面对模拟真实访客的CC攻击就没有什么特别的办法,只要攻击者给你的网页带上?参数,CF就会回源去读取你服务器上数据,同时有个几千几万请求的时候,就会有很多CF的服务器回源到你服务器上,造成拥堵,网站就无法访问了,在你服务器上查看连接的IP也都是CF的IP,这种情况下也不可能去通过封禁CF IP的方式来达到防攻击的目的,因为你把CDN的IP都给封杀了,正常访客通过这个CDN就访问不到你的网站,相当于变相宕机了。


所以问题点就在于,在我们的后端服务器(源服务器)上一定要有个判断方法,能识别到每个回源访问的真实IP地址是什么,幸好现在大部分CDN,包括国内的阿里云,腾讯云,百度云都是支持携带X-Forwarded-For头的,这个识别头会带上访客的真实IP来传给服务器,因此可以通过这个来识别到每个访问请求的真实IP,从而通过封禁访问量过大的IP的方法达到防CC的目的,一般不会误杀,正常访客不大可能达到非常高频率的访问,高频访问要嘛是攻击者要嘛就是采集者。


先来看看两种方式访问路径

普通用户IE浏览器  ——->  你的服务器      由于普通用户直接访问你的服务器,所以服务器端可以直接得到用户的 IP 地址, 而我们的限制就是基于对 来源IP 地址的访问限制

普通用户浏览器  —–>  CDN或者反代服务器(我们自己建的反代服务器,CloudFlare,阿里云等) —-> 源服务器(PHP 程序部署在这里,iptables, nginx 安全配置等)


第一种方式可以直接看到访客IP,因此封禁比较简单,只要看看日志,挑出量大的直接封杀就行了

## 用户的 IP 地址 $binary_remote_addr 作为 Key,每个 IP 地址最多有 50 个并发连接

## 你想开 几千个连接 刷死我? 超过 50 个连接,直接返回 503 错误给你,根本不处理你的请求了

在 Nginx 的 http 模块内加入如下配置

limit_conn_zone $binary_remote_addr zone=TotalConnLimitZone:10m ;
limit_conn  TotalConnLimitZone  50;
limit_conn_log_level notice;
 
## 用户的 IP 地址 $binary_remote_addr 作为 Key,每个 IP 地址每秒处理 10 个请求
## 你想用程序每秒几百次的刷我,没戏,再快了就不处理了,直接返回 503 错误给你
limit_req_zone $binary_remote_addr zone=ConnLimitZone:10m  rate=10r/s;
limit_req_log_level notice;
 
## 具体服务器配置
server {
    listen   80;
    location ~ .php$ {
                ## 最多 5 个排队, 由于每秒处理 10 个请求 + 5个排队,你一秒最多发送 15 个请求过来,再多就直接返回 503 错误给你了
        limit_req zone=ConnLimitZone burst=5 nodelay;
 
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        include    fastcgi_params;
    }    
 
}

第二种方式因为日志中记录到的访客IP其实都是CDN的IP地址,如果用第一种的NGINX配置脚本,最终封杀的是CDN 的IP而不是攻击者,因此需要迂回获取访客真实IP,下面就讲讲怎么获取访客真实IP

X-Forwarded-For :  用户IP, 代理服务器IP

如果中间经历了不止一个 代理服务器,像访问www.bnxb.com 中间建立多层代理之后,这个 记录会是这样

X-Forwarded-For :  用户IP, 代理服务器1-IP, 代理服务器2-IP, 代理服务器3-IP, ….

可以看到经过好多层代理之后, 用户的真实IP 在第一个位置, 后面会跟一串 中间代理服务器的IP地址,从这里取到用户真实的IP地址,针对这个 IP 地址做限制就可以了,

nginx 配置 取得用户的原始地址,在 Nginx 的 http 模块内加入如下配置

map $http_x_forwarded_for  $clientRealIp
{
        ## 没有通过代理,直接用 remote_addr
    ""    $remote_addr;  
        ## 用正则匹配,从 x_forwarded_for 中取得用户的原始IP
        ## 例如   X-Forwarded-For: 202.123.123.11, 208.22.22.234, 192.168.2.100,...
        ## 这里第一个 202.123.123.11 是用户的真实 IP,后面其它都是经过的 CDN 服务器
    ~^(?P<firstAddr>[0-9.]+),?.*$    $firstAddr;
}
## 通过 map 指令,我们为 nginx 创建了一个变量 $clientRealIp ,这个就是 原始用户的真实 IP 地址,
## 不论用户是直接访问,还是通过一串 CDN 之后的访问,我们都能取得正确的原始IP地址

这里$clientRealIP 就是用户真实 IP,而且代码中还配合使用了 $remote_addr,因此$clientRealIP 还能兼容上文中第一种直接访问模式,不像 $http_x_forwarded_for 在直接访问模式中将会是空值!所以 $clientRealIP 还能配置到 Nginx 日志格式中,替代传统的 $remote_addr 使用,这样日志显示也能正常显示真实访客IP


下面是修改之后的 Nginx 配置: CDN环境下 Nginx 的安全配置

## 这里取得原始用户的IP地址
map $http_x_forwarded_for  $clientRealIp {
    ""    $remote_addr;
    ~^(?P<firstAddr>[0-9.]+),?.*$    $firstAddr;
}
 
## 针对原始用户 IP 地址做限制
limit_conn_zone $clientRealIp zone=TotalConnLimitZone:20m ;
limit_conn  TotalConnLimitZone  50;
limit_conn_log_level notice;
 
## 针对原始用户 IP 地址做限制
limit_req_zone $clientRealIp zone=ConnLimitZone:20m  rate=10r/s;
#limit_req zone=ConnLimitZone burst=10 nodelay;
limit_req_log_level notice;
 
## 具体服务器配置
server {
    listen   80;
    location ~ .php$ {
                ## 最多 5 个排队, 由于每秒处理 10 个请求 + 5个排队,你一秒最多发送 15 个请求过来,再多就直接返回 503 错误给你了
        limit_req zone=ConnLimitZone burst=5 nodelay;
 
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        include    fastcgi_params;
    }    
 
}

到这里你的网站就能配合任何CDN或者云加速之类的平台完成防御作业了

顶(6)
踩(0)

您可能还会对下面的文章感兴趣:

最新评论