HTTP X-Forwarded-For 引见

发布日期:2019-07-23 19:56:15 阅读数: 936次 来源: 作者:

X-Forwarded-For 是一个 HTTP 扩展头部。HTTP/1.1(RFC 2616)和谈并没有对它的定义,它最起头是由 Squid 这个缓存代办署理软件引入,用来暗示 HTTP 请求端实在 IP。现在它曾经成为现实上的尺度,被各大 HTTP 代办署理、负载平衡等转发办事普遍利用,并被写入 RFC 7239(Forwarded HTTP Extension)尺度之中。

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 请求。分歧言语获取 Remote Address 的体例纷歧样,例如 php 是 $_SERVER["REMOTE_ADDR"],Node.js 是 req.connection.remoteAddress,但道理都一样。


问题

有了上面的布景学问,起头说问题。我用 Node.js 写了一个最简单的 Web Server 用于测试。HTTP 和谈跟言语无关,这里用 Node.js 只是为了便利演示,换成任何其他言语都能够获得不异结论。别的本文用 Nginx 也是一样的事理,若是有乐趣,换成 Apache 或其他 Web Server 也一样。

下面这段代码会监听 9009 端口,并在收到 HTTP 请求后,输出一些消息:

var http = require('http');

http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.write('remoteAddress: ' + req.connection.remoteAddress + '\n');
    res.write('x-forwarded-for: ' + req.headers['x-forwarded-for'] + '\n');
    res.write('x-real-ip: ' + req.headers['x-real-ip'] + '\n');
    res.end();
}).listen(9009, '0.0.0.0');

这段代码除了前面引见过的 Remote Address 和 X-Forwarded-For,还有一个 X-Real-IP,这又是一个自定义头部字段。X-Real-IP 凡是被 HTTP 代办署理用来暗示与它发生 TCP 毗连的设备 IP,这个设备可能是其他代办署理,也可能是真正的请求端。需要留意的是,X-Real-IP 目前并不属于任何尺度,代办署理和 Web 使用之间能够商定用任何自定义头来传送这个消息。

此刻能够用域名 + 端标语间接拜候这个 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 转发过的办事。

测试间接拜候 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。

再来拜候 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 设置装备摆设中的这两行起感化了,为请求额外添加了两个自定义头:

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

现实上,在出产情况中摆设 Web 使用,一般都采用上面第二种体例,有良多益处。但这就引入一个隐患:良多 Web 使用为了获取用户真正的 IP,从 HTTP 请求头中获取 IP。

HTTP 请求头能够随便机关,我们通过 curl 的 -H 参数机关 X-Forwarded-For 和 X-Real-IP,再来测试一把。

间接拜候 Node.js 办事:

curl http://t1.imququ.com:9009/ -H 'X-Forwarded-For: 1.1.1.1' -H 'X-Real-IP: 2.2.2.2'

remoteAddress: 114.248.238.236
x-forwarded-for: 1.1.1.1
x-real-ip: 2.2.2.2

对于 Web 使用来说,X-Forwarded-For 和 X-Real-IP 就是两个通俗的请求头,天然就不做任何处置原样输出了。这申明,对于直连摆设体例,除了从 TCP 毗连中获得的 Remote Address 之外,请求头中照顾的 IP 消息都不克不及信。

拜候 Nginx 转发过的办事:

curl http://t1.imququ.com/ -H 'X-Forwarded-For: 1.1.1.1' -H 'X-Real-IP: 2.2.2.2'

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

这一次,Nginx 会在 X-Forwarded-For 后追加我的 IP;并用我的 IP 笼盖 X-Real-IP 请求头。这申明,有了 Nginx 的加工,X-Forwarded-For 最初一节以和 X-Real-IP 整个内容无法机关,能够用于获取用户 IP。

用户 IP 往往会被利用与跟 Web 平安相关的场景上,例如查抄用户登录地域,基于 IP 做拜候频次节制等等。这种场景下,确保 IP 无法机关更主要。颠末前面的测试和阐发,对于间接面向用户摆设的 Web 使用,必需利用从 TCP 毗连中获得的 Remote Address;对于摆设了 Nginx 如许反向代办署理的 Web 使用,在准确设置装备摆设了 Set Header 行为后,能够利用 Nginx 传过来的 X-Real-IP 或 X-Forwarded-For 最初一节(现实上它们必然等价)。

那么,Web 使用本身若何判断请求是间接过来,仍是由可控的代办署理转发来的呢?在代办署理转发时添加额外的请求头是一个法子,可是不怎样安全,由于请求头太容易机关了。若是必然要这么用,这个自定义头要够长够稀有,还要保管好不克不及泄显露去。

判断 Remote Address 是不是当地 IP 也是一种法子,不外也不完美,由于在 Nginx 所处办事器上拜候,无论直连仍是走 Nginx 代办署理,Remote Address 都是 127.0.0.1。这个问题还好凡是能够忽略,更麻烦的是,反向代办署理办事器和现实的 Web 使用纷歧定摆设在统一台办事器上。所以更合理的做法是收集所有代办署理办事器 IP 列表,Web 使用拿到 Remote Address 后一一比对来判断是以何种体例拜候。

凡是,为了简化逻辑,出产情况会封掉通过带端口间接拜候 Web 使用的形式,只答应通过 Nginx 来拜候。那是不是如许就没问题了呢?也不见得。

起首,若是用户真的是通过代办署理拜候 Nginx,X-Forwarded-For 最初一节以和 X-Real-IP 获得的是代办署理的 IP,平安相关的场景只能用这个,但有些场景如按照 IP 显示地点地气候,就需要尽可能获得用户实在 IP,这时候 X-Forwarded-For 中第一个 IP 就能够排上用场了。这时候需要留意一个问题,仍是拿之前的例子做测试:

curl http://t1.imququ.com/ -H 'X-Forwarded-For: unknown, <>"1.1.1.1'

remoteAddress: 127.0.0.1
x-forwarded-for: unknown, <>"1.1.1.1, 114.248.238.236
x-real-ip: 114.248.238.236

X-Forwarded-For 最初一节是 Nginx 追加上去的,但之前部门都来自于 Nginx 收到的请求头,这部门用户输入内容完全不成信。利用时需要非分特别小心,合适 IP 格局才能利用,否则容易激发 SQL 注入或 XSS 等平安缝隙。

结论

  1. 间接对外供给办事的 Web 使用,在进行与平安相关的操作时,只能通过 Remote Address 获取 IP,不克不及相信赖何请求头;
  2. 利用 Nginx 等 Web Server 进行反向代办署理的 Web 使用,在设置装备摆设准确的前提下,要用 X-Forwarded-For 最初一节 或 X-Real-IP 来获取 IP(由于 Remote Address 获得的是 Nginx 地点办事器的内网 IP);同时还该当禁止 Web 使用间接对外供给办事;
  3. 在与平安无关的场景,例如通过 IP 显示地点地气候,能够从 X-Forwarded-For 靠前的位置获取 IP,可是需要校验 IP 格局合法性;

PS:网上有些文章建议如许设置装备摆设 Nginx,其实并不合理:

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

如许设置装备摆设之后,平安性确实提高了,可是也导致请求达到 Nginx 之前的所有代办署理消息都被抹掉,无法为真正利用代办署理的用户供给更好的办事。仍是该当弄大白这两头的道理,具体场景具体阐发。

本文由亚博手机app编辑整理亚博手机app