前段时间搭建了一个图床站,然后很自然的套上了一层Cloudflare。但是几天前突然发现大量垃圾评论,想要有针对性的处理一下。于是问题来了,获取不到访客的真实IP,只能看到Cloudflare的IP,想要对IP进行封杀似乎有些问题。
经过十分钟的探索,我总结出来以下几种办法
通过X-Forwarded-For 或 CF-Connecting-IP 标头
Cloudflare会按照行业标准把访问者的真实IP包含在这两个访问头中。那么我们就可以通过提取这个内容来获取到真实IP,不过这个头是可以伪造的,所以实际上还需要进行进一步的处理。
用PHP的话,以下两者之一就可以获取到真实IP:
$_SERVER["HTTP_CF_CONNECTING_IP"] $_SERVER["HTTP_X_FORWARDED_FOR"]
简单的判断一下是这样的:
user_ip = (isset($_SERVER["HTTP_CF_CONNECTING_IP"])$_SERVER["HTTP_CF_CONNECTING_IP"]:$_SERVER['REMOTE_ADDR']);
但是这样其实有些不方便,需要我们手动改代码,在使用某些开源程序时对应的位置可能不太好找。
多说一句:
有些时候我们的应用可能是由Nginx反代的,这个时候Nginx会记录访客的IP,但是我们的应用记录的只可能是127.0.0.1,这个时候应用想获取到真实IP该怎么办呢?Nginx中可以这样配置:
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
我们用x-real-ip
就能拿到用户的IP啦,x-forwarded-for
能看到每层连接的IP(自然也包括真实IP了)
使用一些插件
对于类似WordPress这类成熟的CMS,有些插件可以提供类似的功能,比如说https://wordpress.org/plugins/real-ip-and-geo-for-cloudflare/
这个可以搜索下看看,同样也需要注意,headers是可以被伪造的
通过Nginx http_realip_module
gx_http_realip_module默认是不被编译的,所以我们需要重新编译Nginx。
参考这篇《为 Nginx 开启 tls 1.3 支持,顺便编译 openssl1.1.1a》,编译参数增加一个--with-http_realip_module
之后创建一个配置文件cloudflare.conf
,IP地址来源于Cloudflare官网
set_real_ip_from 103.21.244.0/22; set_real_ip_from 103.22.200.0/22; set_real_ip_from 103.31.4.0/22; set_real_ip_from 104.16.0.0/12; set_real_ip_from 108.162.192.0/18; set_real_ip_from 131.0.72.0/22; set_real_ip_from 141.101.64.0/18; set_real_ip_from 162.158.0.0/15; set_real_ip_from 172.64.0.0/13; set_real_ip_from 173.245.48.0/20; set_real_ip_from 188.114.96.0/20; set_real_ip_from 190.93.240.0/20; set_real_ip_from 197.234.240.0/22; set_real_ip_from 198.41.128.0/17; set_real_ip_from 2400:cb00::/32; set_real_ip_from 2405:b500::/32; set_real_ip_from 2606:4700::/32; set_real_ip_from 2803:f800::/32; set_real_ip_from 2c0f:f248::/32; set_real_ip_from 2a06:98c0::/29; # use any of the following two real_ip_header CF-Connecting-IP; #real_ip_header X-Forwarded-For;
然后在对应站点的配置文件中,或者是nginx.conf
中添加一行include cloudflare.conf;
即可。
这样Web应用就会获取到正确的IP了,日志里的记录也是正确的IP
这样看谁不爽就添加到黑名单好了。
如何伪造X-Forwarded-For等请求头
用curl的话可以这样:
curl http://example.com -H 'X-Forwarded-For: 1.1.1.1' -H 'X-Real-IP: 2.2.2.2'
或者Postman中设置Headers也一样。
需要注意的是:
- 如果由Nginx直连的应用,那么
REMOTE_ADDR
一定是真实IP,要不怎么进行TCP握手呢 - 如果由Nginx反代,那么需要我们配置合理的
proxy_set_header
设置请求头,此时REMOTE_ADDR
会变成Nginx所属的那台服务器的IP - 由于请求头是可以伪造的、不可控的,所以用
X-Forwarded-For
时总得校验下是不是IP吧,要不可能会出现注入,跨站等安全风险。
参考资料
https://stackoverflow.com/questions/14985518/cloudflare-and-logging-visitor-ip-addresses-via-in-php
如果你是nginx直连,那么REMOTE_ADDR一定是真实IP; 如果套了层cf,那你配置好cf的gx_http_realip_module 之后,nginx肯定会拿到真正的IP的。