nginx real_ip_header 和 X-Forwarded-For 似乎是错误的

Kir*_*oll 64 nginx reverse-proxy http-headers

HTTP标头的维基百科描述X-Forwarded-For是:

X-Forwarded-For:client1、proxy1、proxy2、...

该指令的 nginx 文档real_ip_header部分内容如下:

该指令设置用于传输替换 IP 地址的标头的名称。
在 X-Forwarded-For 的情况下,此模块使用X-Forwarded-For 标头中的最后一个ip 进行替换。[强调我的]

这两种描述似乎相互矛盾。在我们的场景中,X-Forwarded-For标头与所描述的完全一样——客户端的“真实”IP 地址是最左边的条目。同样,nginx 的行为是使用最右边的值——很明显,它只是我们的代理服务器之一。

我的理解X-Real-IP是它应该用于确定实际的客户端 IP 地址——而不是代理。我是否遗漏了什么,或者这是 nginx 中的错误?

而且,除此之外,是否有人对如何使X-Real-IP标题显示最左边的值有任何建议,如 的定义所示X-Forwarded-For

小智 112

我相信在链接多个 IP 时解决 X-Forwarded-For 问题的关键是最近引入的配置选项real_ip_recursive(在 nginx 1.2.1 和 1.3.0 中添加)。来自nginx realip 文档

如果启用递归搜索,则与受信任地址之一匹配的原始客户端地址将替换为请求标头字段中发送的最后一个非受信任地址。

默认情况下,nginx 会抓取链中的最后一个 IP 地址,因为这是唯一一个被认为受信任的 IP 地址。但是使用新的real_ip_recursive启用和多个set_real_ip_from选项,您可以定义多个受信任的代理,它将获取最后一个不受信任的 IP。

例如,使用此配置:

set_real_ip_from 127.0.0.1;
set_real_ip_from 192.168.2.1;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
Run Code Online (Sandbox Code Playgroud)

和 X-Forwarded-For 标头导致:

X-Forwarded-For: 123.123.123.123, 192.168.2.1, 127.0.0.1
Run Code Online (Sandbox Code Playgroud)

nginx 现在会选择 123.123.123.123 作为客户端的 IP 地址。

至于为什么nginx不只是选择最左边的IP地址,而是要求你明确定义可信代理,这是为了防止简单的IP欺骗。

假设客户端的真实 IP 地址是123.123.123.123. 我们还假设客户端做得不好,并且他们试图将其 IP 地址欺骗为11.11.11.11. 他们向服务器发送一个请求,这个请求头已经准备好了:

X-Forwarded-For: 11.11.11.11
Run Code Online (Sandbox Code Playgroud)

由于反向代理只是将 IP 添加到这个 X-Forwarded-For 链中,假设当 nginx 到达它时它最终看起来像这样:

X-Forwarded-For: 11.11.11.11, 123.123.123.123, 192.168.2.1, 127.0.0.1
Run Code Online (Sandbox Code Playgroud)

如果您只是获取最左边的地址,那么客户端就可以轻松地欺骗其 IP 地址。但是在上面的 nginx 配置示例中,nginx 只会信任最后两个地址作为代理。这意味着 nginx 将正确选择123.123.123.123IP 地址,尽管欺骗 IP 实际上是最左边的。


Sha*_*den 8

X-Forwarded-Fornginx real_ip 模块的头部解析确实存在缺陷。

len = r->headers_in.x_forwarded_for->value.len;
ip = r->headers_in.x_forwarded_for->value.data;

for (p = ip + len - 1; p > ip; p--) {
  if (*p == ' ' || *p == ',') {
    p++;
    len -= p - ip;
    ip = p;
    break;
  }
}
Run Code Online (Sandbox Code Playgroud)

它从标题字符串的最右侧开始,一旦看到空格或逗号,它就会停止查找并将该部分粘贴到 IP 变量中空格或逗号的右侧。因此,它将最近的代理地址视为原始客户端地址。

根据规范,它的表现不佳;这就是没有在 RFC 中以痛苦的明显术语阐明它的危险。

旁白:甚至很难找到有关该格式的良好主要来源,该格式最初是由 Squid 定义的 - 翻阅他们的文档可以确认顺序;最左边是原始客户端,最右边是最近的追加。我非常想在那个维基百科页面上添加一个[需要引用]。一个匿名编辑似乎是互联网在这个主题上的权威。

如果可能,您是否可以让您的中间代理停止将自己添加到标头的末尾,而仅将其保留为真实的客户端地址?

  • @IanKemp,不,**end** 是正确的。对于代理的服务器端,**请求发起者**(即_TCP_请求)是前一个代理(如果有的话)。之前的代理可能已经发送了一个“X-Forwarded-For”标头,左边可能是原始客户端地址,并且可能附加了任何前面的代理。因此,当前服务的代理会将先前的代理(= 发起者)添加到该列表的末尾,并将因此增加的“X-Forwarded-For”标头提供给下一个上游跳。当然,他们本可以选择更明显的措辞。 (3认同)

小智 5

X-Real-IP 是服务器与之通信的实际客户端(服务器的“真实”客户端)的 IP 地址,在代理连接的情况下,它是代理服务器。这就是为什么 X-Real-IP 将包含 X-Forwarded-For 标头中的最后一个 IP。

  • 不,我的意思是,为什么`X-Real-IP` 会返回我自己的代理服务器的 IP 地址*永远*有用? (3认同)
  • 好的,但是,对我来说,这根本就不是有用的信息。我想获得客户端的原始 IP 地址——这是至关重要的,根据我所阅读的所有内容,这些标头的目的。为什么我想知道我们代理服务器的 IP 地址? (2认同)