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或者云加速之类的平台完成防御作业了
- 最新评论