Skip to content

获取客户端“真实”IP #252

Open
@yaofly2012

Description

@yaofly2012

背景

WEB服务总是对客户端的IP很感兴趣:地理统计、地理定位、审计、速率限制、滥用阻止、会话历史等。

如何获取客户端IP

1. req.connection.remoteAddress

1.1 什么是remoteAddress

和服务器建立TCP连接的客户端IP,这里的客户端不是发起请求的最初的客户端,可能是代理服务器。

1.2 req.connection.remoteAddress, req.socket.remoteAddressreq.connection.socket.remoteAddress区分

这三个属性都是获取remoteAddress,但由于nodejs版本问题导致使用上稍稍不同:

As of node 0.4.7, it seemshttp has remoteAddress 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 小结

  1. remoteAddress是TCP三次握手的IP,是无法修改的;
  2. 如果客户端进过了代理,服务器通过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):
image

2.3 小结

  1. X-Forwarded-For首部是由代理服务器设置的(客户端也可以设置),有些代理可能不遵守更新规则,甚至伪造该首部。
  2. 服务器是无法利用X-Forwarded-For首部获取到上一个服务器节点的IP地址的。
    职责所在呀,服务器有其他方式获取上一个节点的IP。

3. X-Real-IP, True-Client-Ip等自定义首部 ???

大部分代理服务器,CDN厂商都自定义一些自定义首部用于记录客户端IP:

  1. CF-Connecting-IP (Cloudflare)
  2. Fastly-Client-Ip (Fastly CDN and Firebase hosting header when forwared to a cloud function)
  3. True-Client-Ip (Akamai and Cloudflare)
  4. X-Real-IP (Nginx proxy/FastCGI)
  5. X-Cluster-Client-IP (Rackspace LB, Riverbed Stingray)
  6. ...

可参考库request-ip的实现。这些自定义首部都是非标准的,他们都表示客户端IP吗?

3.1 X-Real-IP

  1. X-Real-IP通常被 HTTP 代理用来表示与它产生 TCP 连接的设备 IP,这个设备可能是其他代理,也可能是真正的请求端。
    即HTTP代理用X-Real-IP告诉下个节点的服务器,自己的remoteAddress是啥,相当于X-Forwarded-For首部最右边的IP。

  2. 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的实现

参考

  1. MDN X-Forwarded-For
  2. X-Forwarded-For 和 X-Real-IP 的区别
  3. How to find out the remote Address in node.js if it is HTTPS request?
  4. request-ip
  5. “真实”客户端 IP 的危险
  6. Nginx Windows详细安装部署教程

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions