一、X-Forwarded-For缺陷与陷阱
X-Forwarded-For:简称XFF头,它代表客户端,也就是HTTP的请求端真实的IP,只有在通过了HTTP代理或者负载均衡服务器时才会添加该项。它不是RFC中定义的标准请求头信息,在squid缓存代理服务器开发文档中可以找到该项的详细介绍。
X-Forwarded-For(XFF)是用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段。 Squid缓存代理服务器的开发人员最早引入了这一HTTP头字段,并由IETF在Forwarded-For HTTP头字段标准化草案中正式提出。
这一HTTP头一般格式如下:
X-Forwarded-For: client1, proxy1, proxy2, proxy3
其中的值通过一个逗号+空格把多个IP地址区分开,最左边(client1)是最原始客户端的IP地址,代理服务器每成功收到一个请求,就把请求来源IP地址添加到右边。在上面这个例子中,这个请求成功通过了3台代理服务器:proxy1, proxy2及 proxy3。请求由client1发出,到达了proxy3(proxy3可能是请求的终点)。请求刚从client1中发出时,XFF是空的,请求被发往proxy1;通过proxy1的时候,client1被添加到XFF中,之后请求被发往proxy2;通过proxy2的时候,proxy1被添加到XFF中,之后请求被发往proxy3;通过proxy3时,proxy2被添加到XFF中,之后请求的的去向不明,如果proxy3不是请求终点,请求会被继续转发。
因为伪造这一字段非常容易,所以应该谨慎使用X-Forwarded-For字段。正常情况下XFF中最后一个IP地址是最后一个代理服务器的IP地址,这通常是一个比较可靠的信息来源。下面会详细说明,在什么情况下,这个信息并不准确。
如果客户端使用了http代理(一般大公司内部上网都需要使用代理),同时,这个代理并没有正确设置(也可能是故意不设置)XFF,那么,XFF中client1的位置就会被http代理的ip取代,那么我们就无**确获取客户端的真实IP。
为了减缓可用的IP地址空间的枯竭,大多数ISP网络或者公司网络中都会使用NAT设备,这样,一个公司或者一个小区的用户就可以共享使用一个IP来实现上网需求。NAT设备工作在IP层和TCP层,所以XFF不管怎么设置也无法解决这个问题。
这种情况下,我们也无法拿到客户的真实IP。
Windows:
Linux
访问以下两个网址,返回的网页会告诉你,你的公网IP是多少
可以用curl访问网址,举个例子
结果,我的真实ip是192.168.1.186(私有IP),但是在XFF中会显示我的IP为223.72.90.206。
有的公司使用加速乐,我们可以认为使用这种服务后,相当于增加了一层http代理。
我们可以查看一下域名是否使用了加速乐(查看其他的安全服务也是类似的方法)
结果,XFF中有一层proxy就是加速乐的IP,由于加速乐有很多节点,所以这个IP地址新并不是固定的。内部消息显示加速乐节点的IP要达到上万个,尽管我太想相信。但是这至少说明一点,就是XFF中proxy的IP地址是多变的。
最近测试发现,使用电信4G网络的时候,XFF会莫名其妙的加入多个proxy代理,但是这个IP地址实在是无法查证,我们姑且认为他们也是一种安全服务吧
攻击者可以很容易伪造XFF,在发出请求时在XFF值中插入多个IP地址,以混淆服务端获取到客户端的真实IP。
结论,从XFF中的获取到的客户端IP地址并不可信,在使用各种ACL策略的时候,需要读者权衡利弊来满足业务需求。
nginx目前在各个互联网公司中的标配,其中ngx_http_realip_module模块就提供从XFF中实现获取用户的真实IP的功能。
但是,上面的配置有一些问题,你发现什么问题了吗?
首先,我们解释一下上面最后一条配置语句的含义
实际上的含义是
我们可以看到, X-Forwarded-For加上$remote_addr。但是有一个问题,realip模块要比proxy模块运行的早,在realip模块中会把remote_addr重置为客户端IP,这样会导致 X-Forwarded-For设置错误。
我们拿proxy3为例,正常情况下,XFF传到proxy4服务时,XFF的值是
如果按照上面的配置的话,XFF值会是
如果内部有两级Nginx的话,XFF值会是
总之,只要是XFF出现的重复的IP地址,就有理由怀疑是Nginx配置引起的。
下面是才是realip的正确配置
remote_addr的副本信息,当realip模块修改了 realip_remote_addr用于保留$remote_addr的原始值。
假设有这样一个XFF
其中,如果proxy2,proxy3是我们已知(可信)代理的话,我们可以确定proxy1的IP地址是可信的。
二、X-Forwarded-For和有效负载均衡
其实LVS进行动态负载均衡,根据客户端访问节点的ip进行分流隔离或者负载均衡,是有问题的。原因是实际上无法分辨客户端源ip是真实ip还是代理IP。另外一般情况下客户端过来的访问都是经过我们这边服务端的反向代理的,此时更需要知道真实的ip。反向代理的例子比如,我访问一下Yahoo Finance查看Tesla的股价:
所以,自己公司DevOps搭建负载均衡服务器的时候,一定要考虑这个问题,如何获得发起请求的客户端的真实IP,这个大多数情况下并不能获得;但是如果没有,就是伪负载均衡,只是对反向代理最前面一层进行了负载均衡,并没有对源ip进行负载均衡。
从上面看到实际上 Remote Address就是反向代理服务器的地址,是HTTP请求的远程/源地址;三次握手就是用的这个地址,ACK时候也是;如果forgery这个那么HTTP响应报文不能收到,所以这个伪造没有任何意义,即 Remote Address默认防伪造;而只有 Remote Address用户的真实IP地址将被丢失,因此有了HTTP扩展头部 X-Forward-For.反向代理服务器转发用户的HTTP请求时,需要将用户的真实IP地址写入到 X-Forward-For中,以便后端服务能够使用。由于 X-Forward-For是可修改的,所以 X-Forward-For中的地址在某种程度上不可信。在进行与安全有关的操作时,只能通过 Remote Address获取用户的IP地址,不能相信任何请求头
X-Forwarded-For在Wikipedia的解释: X-Forwarded-For
也就是LB和代理一般是为了降低外部带宽,一般我们都用了反向代理,属于transparent proxy;
注:
X-Forward-For跟 Referer和 User-Agent一样,都是 HTTP中的头域。HTTP/1.1的 RFC文档编号为 2616,在 2616中并未提及 X-Forward-For,也就是说 HTTP/1.1出现的时候 X-Forward-For还没出生。真正提出 X-Forward-For的是2014年的 RFC7239(详见 https://www.rfc-editor.org/rfc/rfc7239.txt X-Forward-For作为HTTP扩展出现;
RFC7239很长,实际上跟我们相关的只有几个部分,例如: 1. Abstract; 7.5. Example Usage
Abstract是本文章的摘要,它描述了 RFC7239的作用:
从这里我们了解到 X-Forward-For的正向用途是便于服务端识别原始 IP,并根据原始 IP作出动态处理。例如服务端按照 IP地址进行负载均衡时,如果能够看破 IP代理,取得原始 IP地址,那么就能够作出有效的负载。否则有可能造成资源分配不均,导致假负载均衡的情况出现。
Example Usage给了一个例子:
X-Forwarded-For格式:
如果一个 HTTP请求到达服务器之前,经过了三个代理 Proxy1、Proxy2、Proxy3,IP分别为 IP1、IP2、IP3,用户真实 IP为 IP0,那么按照 XFF标准,服务端最终会收到以下信息:
所以,不论是从代理服务器第一层还是最后一层,都需要收集真实的IP才能进行真正的负载均衡
三、X-Forwarded-For的格式
这一HTTP头一般格式如下:
X-Forwarded-For: client1, proxy1, proxy2, proxy3
其中的值通过一个逗号+空格把多个IP地址区分开,最左边(client1)是最原始客户端的IP地址,代理服务器每成功收到一个请求,就把请求来源IP地址添加到右边。在上面这个例子中,这个请求成功通过了三台代理服务器:proxy1, proxy2及 proxy3。请求由client1发出,到达了proxy3(proxy3可能是请求的终点)。请求刚从client1中发出时,XFF是空的,请求被发往proxy1;通过proxy1的时候,client1被添加到XFF中,之后请求被发往proxy2;通过proxy2的时候,proxy1被添加到XFF中,之后请求被发往proxy3;通过proxy3时,proxy2被添加到XFF中,之后请求的的去向不明,如果proxy3不是请求终点,请求会被继续转发。
鉴于伪造这一字段非常容易,应该谨慎使用X-Forwarded-For字段。正常情况下XFF中最后一个IP地址是最后一个代理服务器的IP地址,这通常是一个比较可靠的信息来源。
关于XFF(X-FORWARDED-FOR),X-Forwarded-For的格式的介绍到此结束,希望对大家有所帮助。