维基百科对HTTP标头的描述X-Forwarded-For
是:
X-Forwarded-For: 客户端1,代理1,代理2,...
该指令的 nginx 文档real_ip_header
部分内容如下:
此指令设置用于传输替换 IP 地址的标头的名称。
对于 X-Forwarded-For,此模块使用最后的ip 在 X-Forwarded-For 标头中进行替换。[重点是我的]
这两种描述似乎互相矛盾。在我们的场景中,X-Forwarded-For
标头完全符合描述——客户端的“真实”IP 地址是最左边的条目。同样,nginx 的行为是使用正确的- 最具价值——显然,这只是我们的代理服务器之一。
我的理解X-Real-IP
是应该用于确定实际的客户端 IP 地址——不是代理。我是否遗漏了什么,或者这是 nginx 的一个错误?
除此之外,有人对如何让X-Real-IP
标题显示左边X-Forwarded-For
-最大价值,如?的定义所示。
答案1
我认为解决多个 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.123
IP 地址,尽管伪造的 IP 实际上位于最左边。
答案2
X-Forwarded-For
nginx real_ip 模块对 header的解析确实存在缺陷。
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 定义 - 通过他们的文档确认顺序;最左边是原始客户端,最右边是最新附加的。我很想添加一个[需要引用]到那个维基百科页面。匿名编辑似乎是互联网上关于这个主题的权威。
如果可能的话,您可以让您的中间代理停止将自己添加到标头的末尾,而只留下真实的客户端地址吗?
答案3
X-Real-IP 是服务器正在与之通信的实际客户端(服务器的“真实”客户端)的 IP 地址,在代理连接的情况下,该客户端是代理服务器。这就是为什么 X-Real-IP 将包含 X-Forwarded-For 标头中的最后一个 IP。
答案4
更多的是警告而不是答案......
我尝试在多个地图位置添加一些 Nginx 缓存服务器,但没有意识到我的主服务器(数据源)已经位于本地运行的 Nginx 缓存服务器后面,有时本地服务器配置为运行 Apache,并将 Nginx 放在其前面充当缓存。
添加第二个单独的 Nginx 缓存服务器将导致两个缓存服务器,因此您的HTTP_X_REAL_IP
或HTTP_X_FORWARDED_FOR
出现错误,显示其中一个服务器的 IP 而不是访问者 IP。
就我而言,我通过设置新的缓存服务器直接从 Apache 端口(在我的情况下是端口 7080)获取数据,绕过源/主服务器上的本地 Nginx 缓存,从而解决了这个问题。
另一个解决方案是删除HTTP_X_REAL_IP
本地缓存服务器。
顺便说一下,我正在运行 Plesk 面板。