先上代码
var http = require('http')
var server = http.createServer(function (req,res) {
console.log(req.headers['x-forwarded-for'] ); // 判断是否有反向代理
console.log(req.socket.remoteAddress ); // 判断后端的 socket 的 IP
let ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress
res.end(ip)
})
server.listen('9098')
x-forwarded-for是什么?
X-Forwarded-For 是一个扩展header头,用来表示 HTTP 请求端真实 IP,在HTTP/1.1(RFC 2616)协议中没有定义,但是现在已经成为事实上的标准,被各大 HTTP 代理、负载均衡等转发服务广泛使用,并被写入 RFC 7239(Forwarded HTTP Extension)标准之中。
由人为设置
一些代理服务器会设置一些消息头,比如nginx会在转发请求的时候可以带上这个消息头,向应用服务传递客户端的真实IP;
使用下面的配置在nginx设置反向代理转发的X-Forwarded-For:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; // 常用
proxy_set_header X-Forwarded-For "$http_x_forwarded_for, $remote_addr" // 不常用,$http_x_forwarded_for是读取到的消息头,如果请求头没有x-Forwarded-for,这个值就是空的。
这样就能在应用服务器拿到消息头 X-Forwarded-For;
可以伪造
因为是人为设置,伪造一个假的很简单,如下:
proxy_set_header X-Forwarded-For "121.12.12.12";
所以接收到的不可信任的服务器传递回来的header,或者客户端伪造了header,其中x-forwarded-for是不能当做客户端IP的。
格式
X-Forwarded-For请求头格式非常简单,就这样:X-Forwarded-For:client, proxy1, proxy2
,由[英文逗号+空格]隔开的多个部分组成,最开始的是离服务端最远的设备IP,然后是每一级代理的IP。
如果一个 HTTP 请求到达服务器之前,经过了三个代理 Proxy1、Proxy2、Proxy3,IP 分别为 IP1、IP2、IP3,用户真实 IP 为 IP0,代理服务器会把前一个网络设备的IP地址追加到X-Forwarded-For 上面,经过层层追加,服务端最终会收到以下信息:
X-Forwarded-For: IP0, IP1, IP2
同时注意到IP3是不会追加上到这个列表上的。
实际验证:在本地电脑开启一个nodejs服务,端口为9090;开启nginx反向代理,端口为8062;
- 通过8062访问nginx服务的时候,req.headers['x-forwarded-for']值为
192.168.1.105 //nginx通过X-Forwarded-For将客户端的地址转发了过来
- 通过9090直接访问nodejs访问,req.headers['x-forwarded-for']值为
undefined //直接请求的时候,没有设置消息头,自然为undefined
所以:
- 有没有X-Forwarded-For和代理服务器的设置有关;
- 正确与否也和代理服务器有关;
remoteAddress
如果没有X-Forwarded-For,应用服务器可以通过与服务端建立 TCP 连接获取到。在nodejs中可以通过req.socket.remoteAddress获取到IP3;
remoteAddress有没有可能是假的呢?因为tcp链接需要三次握手,所以无法伪造这个ip。
X-Real-IP是什么
是一个自定义的消息头,目前并不属于任何标准,完全由用户控制。
结论;
没有代理:直接使用remoteAddress获取客户端IP,因为header中x-forwarded-for不可靠,可能有也可能没有,甚至可能是伪造的;
有代理的情况下,获取到的remoteAddress是代理服务器的IP,如果代理服务器是可信赖的,那么能通过x-forwarded-for来获取客户端IP。
在express,koa中都有获取ip的方法,他们是怎么封装的呢?
koa中是如何封装获取客户端IP的?
express中是如何处理IP的?
如有错误之处,望请斧正。