参考文档:

HTTP 请求头中的 X-Forwarded-For

Java面试-如何获取客户端真实IP

1. X-Forwarded-For

通过名字就知道,X-Forwarded-For 是一个 HTTP 扩展头部。

X-Forwarded-For 请求头格式非常简单,就这样:

X-Forwarded-For: client, proxy1, proxy2

可以看到,XFF 的内容由「英文逗号 + 空格」隔开的多个部分组成,最开始的是离服务端最远的设备 IP,然后是每一级代理设备的 IP。

如果一个 HTTP 请求到达服务器之前,经过了三个代理 Proxy1、Proxy2、Proxy3,IP 分别为 IP1、IP2、IP3,用户真实 IP 为 IP0,那么按照 XFF 标准,服务端最终会收到以下信息:

X-Forwarded-For: IP0, IP1, IP2

Proxy3 直连服务器,它会给 XFF 追加 IP2,表示它是在帮 Proxy2 转发请求。列表中并没有 IP3,IP3 可以在服务端通过 Remote Address 字段获得。我们知道 HTTP 连接基于 TCP 连接,HTTP 协议中没有 IP 的概念,Remote Address 来自 TCP 连接,表示与服务端建立 TCP 连接的设备 IP,在这个例子里就是 IP3。

Remote Address 无法伪造,因为建立 TCP 连接需要三次握手,如果伪造了源 IP,无法建立 TCP 连接,更不会有后面的 HTTP 请求。

2. nginx 代理 ip

2.1 直接访问

这段代码除了前面介绍过的 Remote Address 和 X-Forwarded-For,还有一个 X-Real-IP,这又是一个自定义头部字段。X-Real-IP 通常被 HTTP 代理用来表示与它产生 TCP 连接的设备 IP,这个设备可能是其他代理,也可能是真正的请求端。需要注意的是,X-Real-IP 目前并不属于任何标准,代理和 Web 应用之间可以约定用任何自定义头来传递这个信息。

测试直接访问 Node 服务:

curl http://t1.imququ.com:9009/

remoteAddress: 114.248.238.236
x-forwarded-for: undefined
x-real-ip: undefined

由于我的电脑直接连接了 Node.js 服务,Remote Address 就是我的 IP。同时我并未指定额外的自定义头,所以后两个字段都是 undefined。

2.2 通过 nginx 访问

现在可以用域名 + 端口号直接访问这个 Node.js 服务,再配一个 Nginx 反向代理:

location / {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-NginX-Proxy true;

    proxy_pass http://127.0.0.1:9009/;
    proxy_redirect off;
}

我的 Nginx 监听 80 端口,所以不带端口就可以访问 Nginx 转发过的服务。

注意:我们配置了

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

再来访问 Nginx 转发过的服务:

curl http://t1.imququ.com/

remoteAddress: 127.0.0.1
x-forwarded-for: 114.248.238.236
x-real-ip: 114.248.238.236

这一次,我的电脑是通过 Nginx 访问 Node.js 服务,得到的 Remote Address 实际上是 Nginx 的本地 IP。

我们一般会采用 nginx 配置,一般服务端获取 ip,都是从 x-forwarded-for 获取第一个 ip

所以我们手动设置 x-forwarded-for 请求头就可以达到伪造 ip 的目的

Google 浏览器可以通过 ModHeader 插件来达到伪造请求头的目的

**添加 ModHeader 插件,配置 X-Forwarded-For 179.25.67.34(ip随意),即可骗过服务器 **