发布于 2 天前
发布于 2 天前
风熙.
更新于 2 天前
0
0
雷池技术博客投稿
该文章同时在我的博客网站刊载,欢迎光临
本文适用情况:用户第一层访问的是CDN,或者是其他可以添加自定义HTTP请求头的,而且代理链路可信不会修改你的自定义请求头,这个方法不论有没有使用雷池,是通用的。
按照本文教程配置成功后可以实现“一头走天下”:只要代理链路不对自定义头修改,任何代理链路和源站都可以直接读取这个头获得访客ip,而不需要繁琐的配置XFF
不想看长文的直接跳转到正片
最近我看到有些同学们配XFF出现了很多问题,依照我早期摸索出的经验,写下这篇文章。
HTTP请求体由三部分组成:请求行、请求头、报文体。
这是一个正常的的HTTP请求
其中,第一行的①、②、③统称为请求行;报文头也称为请求头
可以看到,一个正常的HTTP请求带有这些参数,接下来我将通过访问哔哩哔哩网站并抓包来详细讲解。
在这个请求体示例中,由于是用的GET方法进行请求,GET请求方法是指:向服务器发出请求,意图从服务器下载东西。因此不含有报文体。
可以看到,在我访问哔哩哔哩的请求头
中,我使用HTTP/1.1
协议通过GET
方法向服务器发出请求;在User-Agent
栏,也就是我们熟称的浏览器UA
中可以看到,我正在使用版本号为537.36的电脑版Edge浏览器进行访问,这个浏览器的内核是Chrome136.0.0.0
,一些手机浏览器访问电脑版网页也是通过伪造浏览器UA来实现。
X-Forward-For
X-Forwarded-For
是相对通用的 HTTP 请求头。
当CDN部署在最外层,HTTP 流量是经过CDN进行代理传回的,由于网络连接被CDN截胡,源站服务器无法得知真正的客户端 IP。
这时代理设备(CDN)会给当前的流量加上一个 X-Forwarded-For
头,里面的内容就是连接这个代理的客户端 IP。
下面这个例子中 HTTP 代理通过 X-Forwarded-For
头告诉服务器,真正的客户端地址是 1.2.3.4
1GET / HTTP/1.1
2Host: example.com
3User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36
4X-Forwarded-For: 1.2.3.4
X-Forwarded-For
实际上是一个链式结构。如果流量经过了多层代理设备,X-Forwarded-For
会记录途径的所有 IP。
下面这个例子中 HTTP 代理通过 X-Forwarded-For
头告诉服务器,流量经过了三层代理,真正的客户端地址是 1.2.3.4
。
第一层代理的是 11.12.13.14
,第二层代理的地址是 21.22.23.24
,第三次代理的地址可以直接通过网络连接获取。
1GET / HTTP/1.1
2Host: example.com
3User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36
4X-Forwarded-For: 1.2.3.4, 11.12.13.14, 21.22.23.24
从网络连接中获取: 当雷池作为最外层代理设备,无其他前置代理时选用
从 X-Forwarded-For
中获取上一级代理的地址:在流量到达雷池之前还有一层代理设备(如 Nginx,CDN 等)时可选用
从 X-Forwarded-For
中获取上上一级代理的地址:在流量到达雷池之前还有两层代理设备(如 Nginx,CDN 等)时可选用
从 X-Forwarded-For
中获取上上上一级代理的地址:在流量到达雷池之前还有三层代理设备(如 Nginx,CDN 等)时可选用
从其他 HTTP Header 中获取
:有几种情况
X-Forwarded-For
头,但是可以通过配置,把 IP
通过其他头发过来HTTP
头X-Forward-For
的链式结构会带来一些问题,比如这位同学
这位同学的CDN请求雷池的请求头中,XFF携带的ip数量不等,原因是用户的请求头中就有残留XFF报头,有的CDN只会在XFF后面加上直接获取的ip,而不会清空重写,这一般是用户存在代理导致的。
一般来说多的前几个是代理ip,cdn添加的是直接从网络连接获取ip,不是这位同学所说的最左边才是真实ip(当然也可能是代理转发前的ip),因此可以通过从后往前读xff获取cdn从网络连接中直接获取的ip
但是还有什么办法,更通俗易懂、更好配置呢?
X-Real-IP
有的同学说,X-Real-IP不会被伪造,经我测试,在一些存在漏洞的CDN中依然可以被伪造,例子如下
第一步,我提前在CDN之前就添加了X-Real-IP
请求头,写入阿里云DNS 的ip223.5.5.5
。在雷池中配置使用X-Real-IP读取访客ip。
这个请求体中包含恶意元素,将使得雷池WAF将拦截我的请求,便于我观察转发后的流量。
发包后,雷池拦截
可以看到,X-Real-IP并没有被清空重写,而是直接就这样发出去了,我CDN是有开传递X-Real-IP的。
这下不得了了,此时攻击者直接变成了223.5.5.5
(阿里云DNS的IP),并且IP 威胁情报-长亭百川云平台甚至可以查到?!
阿里云对不起。。。
在CDN控制台中,添加请求报头,报头名称你可以自定义,这里我使用X-Real-Forwarded-For
作为我自定义的请求报头。
值填${remoteAddr}
,意为CDN通过网络连接直接获取的ip地址。
没有装雷池的可以直接往下看,由于自定义报头不存在链式结构,因此每一层都可以直接通过这个头获得ip。
在雷池——防护应用——高级配置中的源IP获取方式中,设置从HTTP Header中获取,值填你自定义的请求报头。
在你的Nginx配置文件中加入以下配置
set_real_ip_from 0.0.0.0/0;
real_ip_header X-Real-Forwarded-For; #填你自定义的请求头
记得要在前面加两个缩进
然后保存
对我的博客进行模拟 SQL 注入攻击并抓包,如何测试防护效果请翻阅雷池官方文档:测试防护效果 | 雷池 SafeLine
可以看到被拦截了,返回了403,我们回雷池看一下日志
可以看到被雷池拦截了,请求报文中带有我们自定义的X-Real-Forwarded-For,记录了我的真实ip。
接下来我进行请求重放,在用户端就伪造这个请求头,模拟这个请求头被黑客知道了的情况
一般别人不可能知道我们自定义的请求头,因为这个请求头只在CDN和雷池(或源站)之间通信,不对外发送和开放。但是为了确保严谨性,我还是进行请求重放测试
我自己手动添加了一个自定义的请求头,模拟被黑客知道然后伪造请求头攻击的情况。
可以看到被雷池BYPASS,回到雷池日志看看情况
可以看到自定义请求头仍然被替换成了访客的真实ip,也就是说通过伪造请求头骗过源站的方式不凑效了。
这样,我们实现了一头走天下:只要代理链路不对这个头修改,任何代理链路都可以直接读取这个头获得访客ip,而不需要繁琐的配置XFF。
为什么我的宝塔面板同时出现雷池和访客的ip?(如下图),为什么日志有时候会显示在访问null
答:因为你在雷池启用了上游服务器健康检查,显示访问路径null是因为包雷池拦了
解决方法:在雷池面板防护应用的应用详情中,点基本信息——基础配置右边的笔,关掉上游服务器健康检查。