Description
背景
WEB服务总是对客户端的IP很感兴趣:地理统计、地理定位、审计、速率限制、滥用阻止、会话历史等。
如何获取客户端IP
1. req.connection.remoteAddress
1.1 什么是remoteAddress
和服务器建立TCP连接的客户端IP,这里的客户端不是发起请求的最初的客户端,可能是代理服务器。
1.2 req.connection.remoteAddress
, req.socket.remoteAddress
和req.connection.socket.remoteAddress
区分
这三个属性都是获取remoteAddress
,但由于nodejs版本问题导致使用上稍稍不同:
As of node 0.4.7, it seems
http
hasremoteAddress
available on:
req.connection.remoteAddress
req.socket.remoteAddress
onhttps
, both of these are undefined, but
req.connection.socket.remoteAddress
does work. That one isn't available on http though, so you need to check carefully. I cannot imagine this behavior is intentional.
综上获取remoteAddress
一般采用这种写法:
function getRemoteAddress(req) {
return req.connection.remoteAddress
|| req.socket.remoteAddress
|| req.connection.socket.remoteAddress
}
1.3 小结
remoteAddress
是TCP三次握手的IP,是无法修改的;- 如果客户端进过了代理,服务器通过
remoteAddress
获取到的最近的代理服务器的IP,并不是最原始客户端IP。
2. HTTP X-Forwarded-For
2.1 引入背景
现代的网络结构中,客户端的请求要经过一个或者多个代理服务器才能到达最终的web服务器。HTTP X-Forwarded-For
首部就是用来记录当前请求已经经历了代理服务器IP。
2.2 语法
X-Forwarded-For: client-ip, proxy1-ip, proxy2-ip
代理服务器在向下一个节点发送请求的时候,把remoteAddress
追加到X-Forwarded-For
首部当前值的后面。
假设客户端(IP=0.0.0.1)向服务器(IP=0.0.0.5)发送请求,中间经过代理1(IP=0.0.0.2),代理2(IP=0.0.0.3),代理3(IP=0.0.0.4):
2.3 小结
X-Forwarded-For
首部是由代理服务器设置的(客户端也可以设置),有些代理可能不遵守更新规则,甚至伪造该首部。- 服务器是无法利用
X-Forwarded-For
首部获取到上一个服务器节点的IP地址的。
职责所在呀,服务器有其他方式获取上一个节点的IP。
3. X-Real-IP
, True-Client-Ip
等自定义首部 ???
大部分代理服务器,CDN厂商都自定义一些自定义首部用于记录客户端IP:
CF-Connecting-IP
(Cloudflare)Fastly-Client-Ip
(Fastly CDN and Firebase hosting header when forwared to a cloud function)True-Client-Ip
(Akamai and Cloudflare)X-Real-IP
(Nginx proxy/FastCGI)X-Cluster-Client-IP
(Rackspace LB, Riverbed Stingray)- ...
可参考库request-ip的实现。这些自定义首部都是非标准的,他们都表示客户端IP吗?
3.1 X-Real-IP
-
X-Real-IP
通常被 HTTP 代理用来表示与它产生 TCP 连接的设备 IP,这个设备可能是其他代理,也可能是真正的请求端。
即HTTP代理用X-Real-IP
告诉下个节点的服务器,自己的remoteAddress
是啥,相当于X-Forwarded-For
首部最右边的IP。 -
X-Real-IP
要比X-Forwarded-For
优先级更高吗?
好像没有固定答案,各种库的实现也行为不一致。
X-Real-IP should probably be preferred over X-Forwarded-For in _extraClientIP directive?
“真实”客户端 IP 的危险
nodejs实现
简单实现:
function getClientIp(req) {
return (req.headers['x-forwarded-for'] || '').split(',')[0]
|| req.connection.remoteAddress
}
比较完整的实现可参考库request-ip的实现