nginx real_ip_header和X-Forwarded-For似乎错了


58

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

Answers:


95

我相信,当多个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;

X-Forwarded-For标头导致:

X-Forwarded-For: 123.123.123.123, 192.168.2.1, 127.0.0.1

现在,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

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

X-Forwarded-For: 11.11.11.11, 123.123.123.123, 192.168.2.1, 127.0.0.1

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


1
非常感谢您,它确实对我有所帮助。这应该是公认的答案。
何塞F. Romaniello

1
根据nginx.org/en/docs/http/ngx_http_realip_module.html,默认情况下,real_ip_header似乎是X-Real-IP,是否意味着恶意用户只能使用随机X-Real-IP发送请求,并将其用作$ remote_addr在nginx中(也可能传递给应用程序)?
gansbrest 2014年

@gansbrest否,因为set_real_ip_from限制了受信任的主机。
El Yobo 2015年

9

X-Forwarded-For头的解析确实在nginx 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;
  }
}

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

根据规格,它的表现不佳;如果没有在RFC中用明显的术语详细说明,则存在危险。

另外:很难找到一种很好的原始格式,该格式最初是由Squid定义的-仔细阅读他们的文档可以确认顺序。最左边是原始客户端,最右边是最新的客户端。我非常想在该维基百科页面上添加[需要引用]。互联网上对此主题的授权似乎是一种匿名编辑

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


感谢您的回复,@ Shane。实际上,当到达nginx时,X-Forwarded-For已经存在。(这是正确的客户端IP地址), 然后nginx本身会继续将负载均衡器的IP地址(前一跳)附加到X-Forwarded-For标头。(大概附加了它所看到的“远程地址”)。如果根本不这样做,我将能够X-Forwarded-For像以前一样使用标头。(我们最近一直在迁移到nginx)
Kirk Woll

@Kirk因此,当nginx获取标头时,它只是原始客户端的地址?但是在处理时,是否将其添加到连接代理服务器的标头上?但这并没有增加-唯一应该碰到该标头的时间是当它通过proxy_pass- 将连接发送到另一个代理时-甚至只有proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;在适当的位置。
Shane Madden

甚至W3C也弄错了:他们的文档指出“代理应将请求发起者的IP地址添加到X-Forwarded-For HTTP标头字段中逗号分隔列表的末尾 ”,它应标明开始
伊恩·肯普

3
@IanKemp,不,结尾是正确的。对于代理的服务器端,请求(即TCP请求)的发起者是前一个代理(如果有)。该先前的代理可能已经发送了一个X-Forwarded-For标头,该标头的左侧可能是原始客户地址,并可能在其后附加了任何先前的代理。因此,当前服务的代理会将前一个代理(=启动器)添加到该列表的末尾,并将由此扩展的X-Forwarded-For标头提供给下一个上游跃点。当然,他们可以选择一个更明显的措词。
blubberdiblub

5

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


1
好的,但是,对我来说,那根本就不是有用的信息。我想获取客户端的原始IP地址-这很关键,并且根据我阅读的所有内容,这些标头的用途是至关重要的。为什么我想知道我们的代理服务器的IP地址?
Kirk Woll

如果对您没有用,那么对您也没有用。没有人强迫您使用X-Real-IP。如果您在应用程序中需要用户的IP,请让您的应用程序解析X-Forwarded-For(由于某些代理(互联网安全设备/防火墙)未设置X-Forwarded-For,因此它并不总是可靠的)对于)。在nginx的上下文中,X-Forwarded-For并不重要,因为除了最后一个条目(X-Real-IP)(它是nginx的客户端)之外,它始终不与这些客户端通信。如果您不需要它,请不要进行设置,取消设置或忽略它:/
user558061 2011年

2
不,我的意思是,为什么会X-Real-IP回到我自己的代理服务器的IP地址永远是有用的?
Kirk Woll

很好..回答人。我一直在寻找这些确切的信息。我需要与代理服务器上的ncat服务器进行通信,因此我需要即时进行此操作。
Yugal Jindle 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.