什么是X-Forwarded-For
X-Forwarded-For (简称XFF)是一个 HTTP 扩展头部,用于表示 HTTP 请求的真实 IP 地址。HTTP/1.1(RFC 2616)协议中并没有对它进行定义,该功能最初由缓存代理软件 Squid 引入。如今,它已成为事实上的标准,广泛应用于各种 HTTP 代理和负载均衡服务中,并被纳入了 RFC 7239(Forwarded HTTP Extension)标准。
X-Forwarded-For 请求头格式如下:
X-Forwarded-For: client, proxy1, proxy2
X-Forwarded-For请求头是由「英文逗号 + 空格」隔开的多个部分组成,最开始的是离服务端最远的设备 IP,然后是每一级代理设备的 IP。
如下图所示,每经过一层代理服务器,X-Forwarded-For中的IP会做一次追加。
代理服务器对X-Forwarded-For的处理
注:为了方便测试X-Forwarded-For的运作原理,这里以最常用的反向代理软件Nginx为例。
在Nginx配置中,有几个关键变量的含义需要了解一下:
- $http_x_forwarded_for:表示X-Forwarded-For值。
- $remote_addr:表示访问来源的IP地址。
- proxy_add_x_forwarded_for:表示“$http_x_forwarded_for”+ “,”+“$remote_addr”,如果$http_x_forwarded_for为空,则这个值与$remote_addr相同。
如果本机Nginx作为反向代理,处理X-Forwarded-For的配置如下:
location / { proxy_pass http://backend; proxy_set_header X-FORWARDED-FOR $proxy_add_x_forwarded_for; proxy_set_header X-FORWARDED-PROTO $scheme; }
上述配置中将$proxy_add_x_forwarded_for 内容设置到X-FORWARDED-FOR属性中,传递给下一个节点(代理服务器或者web服务器)。
WEB服务器对X-FORWARDED-FOR处理
注:WEB服务器以tomcat为例说明。
在Tomcat中,一般通过下面的配置来获取X-FORWARDED-FOR中的IP信息:
<Valve className="org.apache.catalina.valves.RemoteIpValve" remoteIpHeader="X-FORWARDED-FOR" protocolHeader="X-FORWARDED-PROTO" internalProxies="127\.0\.0\.1|2\.2\.2\.2|3\.3\.3\.3"/>
参数解释如下:
- className: 指定了使用的类名。在这个例子中,
org.apache.catalina.valves.RemoteIpValve
类用于处理客户端 IP 地址的重写。 - remoteIpHeader: 指定了包含原始客户端 IP 地址的 HTTP 头名称。通常反向代理会将原始客户端的 IP 地址放入
X-FORWARDED-FOR
头中,因此这里设置为X-FORWARDED-FOR
。 - protocolHeader: 指定了包含原始协议(HTTP 或 HTTPS)的 HTTP 头名称。
X-FORWARDED-PROTO
通常被用于指示原始请求是通过 HTTP 还是 HTTPS 进行的。 - internalProxies: 用于指定一系列内部代理服务器的 IP 地址(通过正则表达式定义)。这些代理服务器会被认为是可信的,并且他们转发的
X-FORWARDED-FOR
头会被 Tomcat 认为是可信的客户端 IP 地址。在这个配置中,internalProxies
列出了三个 IP 地址:127.0.0.1
: 本地回环地址2.2.2.2
,3.3.3.3
: 假设这些是其他可信任的内部代理服务器的 IP 地址。
基于上述配置,Tomcat会针对X-FORWARDED-FOR传递过来的内容从右向左进行逐个检查,如果地址在internalProxies中将去除该IP;如果地址不在internalProxies中则取这个IP作为remoteip。正常来说所有代理服务器的IP应该包含在internalProxies,否则最终可能取到代理服务器的IP。
以文中示例图所示,从1.1.1.1经过2.2.2.2、3.3.3.3两层代理,WEB服务器(4.4.4.4)最终接收到的X-FORWARDED-FOR值为“1.1.1.1, 2.2.2.2, 3.3.3.3”,如果internalProxies=”127\.0\.0\.1|2\.2\.2\.2″,则Tomcat取到的remoteip是3.3.3.3,而不会取到1.1.1.1 。
如何测试?
可以通过curl做X-FORWARDED-FOR模拟测试:
curl -H "X-Forwarded-For: 203.0.113.195, 192.168.17.19" https://www.mailabc.cn
在Nginx配置中,可以针对输出的日志属性进行定义,加入$http_x_forwarded_for变量内容输出,如下示例所示:
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for" ' '$upstream_response_time $request_time ';
这样就可以通过日志查看上游代理节点传递的X-FORWARDED-FOR值具体是什么。当然也可以通过网络抓包分析。
注意事项
X-FORWARDED-FOR 可以在数据传输过程中被修改,属于不可信数据。配置过程中需要注意做好代理节点IP信任控制,防止恶意攻击。