IP 追溯主要用于获取请求的真实 IP ,因为现有服务是基于 Nginx 实现负载均衡的,所以获取请求真实 IP 存在必定难度。在 Matrix 现有实现中,IP 追溯由 getIp
函数完成,其具体代码以下。javascript
/** * 得到请求发送方的 ip * @param {Context} ctx * @return {string} */
export function getIp(ctx) {
const xRealIp = ctx.get('X-Real-Ip');
const { ip } = ctx;
const { remoteAddress } = ctx.req.connection;
return xRealIp || ip || remoteAddress;
}
复制代码
在对代码进行更为详细的讨论以前,咱们须要分析几种经常使用获取请求 IP 来源的方式:html
req.socket.remoteAddress
X-Forwarded-For
X-Real-IP
在 Node.js 官方文档 net_socket_remoteaddress 中,咱们得知可经过 req.socket.remtoeAddress
获取 Socket 链接的源 IP 地址信息。java
socket.remoteAddress
Added in: v0.5.10node
- String
The string representation of the remote IP address. For example,
'74.125.127.100'
or'2001:4860:a005::68'
. Value may beundefined
if the socket is destroyed (for example, if the client disconnected).nginx
根据官方文档 http_request_socket 的描述,req.connection
和 req.socket
是等价的,咱们也能够经过 req.connection.remoteAddress
获取 Socket 链接的源 IP 信息。这种获取请求 IP 来源的方式,适用于客户端直连服务端的场景。git
但因为现有 Matrix 服务使用了 Nginx 做为服务集群的负载均衡,故经过 req.socket.connection
获取获得的是 Nginx 的 IP 地址,并非请求的真实 IP 地址。github
根据 RFC 7239 规范,HTTP 代理(如 Nginx、Apache 等)会改写 HTTP 请求头部,添加 X-Forwarded-For
字段,用于追踪请求的来源,该字段的格式以下:api
X-Forwarded-For: client, proxy1, proxy2
复制代码
下面对 X-Forwarded-For
字段的处理过程,进行详细阐述:浏览器
X-Forwarded-For
字段。X-Forwared-For
字段,而且将值设置为客户端的 IP 地址。X-Forwarded-For
字段值中追加上一代理的 IP 地址。X-Forwarded-For
值的最左边的 IP 地址获取客户端的 IP 地址。但遗憾的是,X-Forwarded-For
是可伪造的:若是客户端在发起 HTTP 请求时,设置了伪造的 X-Forwarded-For
字段,因为后续的每层代理只会追加此字段而不会覆盖,故业务服务器最终获取的客户端 IP 地址多是客户端伪造的地址。安全
一般,咱们须要进行额外的配置,才能使得 Nginx 支持 X-Forwarded-For
。
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
复制代码
HTTP 代理服务器可在请求头部中设置 X-Real-IP
请求源信息,但这并非 RFC 规范中的内容。
X-Real-IP
是客户端不可伪造的,但仅能描述最近一个代理的真实 IP ,若是有多层代理,其仍旧不可做为客户端请求的真实 IP 。在现实生活中,多层代理实际上是比较少见的,常见的是单层代理,所以在一般状况下,使用 X-Real-IP
是足以完成任务的,且相对于 X-Forwarded-For
有更好的安全性。
咱们可经过如下设置,使得 Nginx 支持 X-Real-IP
字段的自动设置。
proxy_set_header X-Real-IP $remote_addr;
复制代码
如下是我对几种获取客户端真实 IP 的方式的总结,分析了各方式的局限和应用场景。
req.socket.remoteAddress | X-Forwarded-For | X-Real-IP | |
---|---|---|---|
是否可伪造 | 否 | 是 | 否 |
有效性 | 仅在客户端直连服务端时 | 仅在未伪造时 | 仅在客户端未通过多级代理时 |