REMOTE_ADDR 可能是空白的吗?

Posted

技术标签:

【中文标题】REMOTE_ADDR 可能是空白的吗?【英文标题】:Is it possible that REMOTE_ADDR could be blank? 【发布时间】:2011-05-15 19:20:16 【问题描述】:

据我所知,网络服务器 (Apache/nginx) 根据请求用户代理的声明位置提供 ($_SERVER['REMOTE_ADDR'])。所以我知道他们可能在撒谎,但这个值有可能是空白的吗?网络接口或网络服务器是否会接受没有正确格式 IP 的请求?

http://php.net/manual/en/reserved.variables.server.php

【问题讨论】:

您不能谎报您希望将数据发送回的位置... TCP 需要知道您的 IP 才能向您发送数据包,因此在他们甚至可以发送 HTTP 请求之前,TCP 连接必须已向 IP 发送 SYN/ACK 以创建连接... 您当然可以谎报您希望将数据发送回的位置。这就是某些形式的 DoS 攻击的问题。 @Xeoncross:带有源欺骗的 DoS 攻击不会像 Apache 那样深入 - 网络堆栈将在连接处于半开状态时卡住(因为无法可靠地生成 @ 987654323@ 没有ACK) - 这甚至是许多 DoS 欺骗攻击的重点:在某些系统上,用于半开连接的表曾经是 tiny 的,一旦被填满,他们就停止接受。无论如何,即使您设法以某种方式打开带有欺骗源地址的 TCP 连接,您看到的只是一个不同的 REMOTE_ADDR,而不是一个空的。 @EJP: 不,REMOTE_ADDR(和REMOTE_PORT)是由本地 Web 服务器根据连接的套接字的远程地址提供的,它不是来自 HTTP 标头。你可能在想$_SERVER['HTTP_X_FORWARDED_FOR'] 我看到REMOTE_ADDR 出现在"unknown" 【参考方案1】:

理论上是可以的,这取决于http服务器或者至少是相应的PHP SAPI。

在实践中,我没有遇到过这种情况,除了 CLI SAPI。

编辑:对于 Apache,它似乎总是设置的,因为 ap_add_common_vars 总是将它添加到最终被 Apache 模块 PHP SAPI 读取的表中(免责声明:我对 Apache 内部的了解非常有限)。

如果在 CGI 环境中使用 PHP,RFC 3875 中的规范似乎保证了这个变量的存在:

4.1.8。远程地址 REMOTE_ADDR 变量必须设置为网络地址 客户端向服务器发送请求。

【讨论】:

这也是我的理论,因为 CLI SAPI 不是由网络服务器处理的 - 可能不会填充该值。但是,我想确保 Apache/Nginx 或网络总是为 IP 传递一个值 - 或者只是放弃请求。 @Xeon 那么这个问题是关于 Apache,而不是 PHP,你应该这样标记它。而且我想无论您使用的是 CGI 还是 apache 模块,它都会有所不同。 实际上,我不使用 Apache - 我使用 Nginx。此外,如问题中所述,我对网络接口、网络服务器或任何其他阻止请求的东西开放,如果这会阻止 PHP 被空 REMOTE_ADDR 调用。【参考方案2】:

是的。我目前在我的 Apache-behind-Nginx 日志中看到“未知”的值,这看起来像是日志中的正常请求/响应序列。我相信这是可能的,因为mod_extract_forwarded 正在根据X-Forwarded-For 标头中的数据修改重置REMOTE_ADDR 的请求。因此,原始的 REMOTE_ADDR 值可能是有效的,但作为通过我们的反向代理和 Apache 的一部分,REMOTE_ADDR 在到达应用程序时似乎无效。

如果你已经安装了 Perl 的libwww-perl,你可以像这样测试这种情况(将 example.com 更改为你自己的域或应用程序):

HEAD -H 'X-Forwarded-For: ' -sSe http://www.example.com/
HEAD -H 'X-Forwarded-For: HIMOM' -sSe http://www.example.com/
HEAD -H 'X-Forwarded-For: <iframe src=http://example.com>' -sSe http://www.example.com/

(您还可以使用任何其他允许您使用自定义请求标头手工制作 HTTP 请求的工具。)

现在,请检查您的访问日志以查看它们记录了哪些值,并检查您的应用程序以了解它们如何处理错误输入。 `

【讨论】:

这太可怕了:用用户提供的 (X-FORWARDED-FOR) 替换规范信息 (REMOTE_ADDR)。不过,您的分析听起来是正确的:“未知”是 valid identifier,而 nginx 可能只是根据其 old proxy header scanning logic 将其从列表中拉出。【参考方案3】:

嗯,它是保留但可写的。我见过写得很糟糕的应用程序在超全局变量上乱涂乱画——脚本是否会覆盖它,例如$_SERVER['REMOTE_ADDR'] = '';?

除此之外,即使请求被代理,也应该有代理的地址 - 可能是某种内部重写模块弄乱了它(mod_rewrite 允许内部重定向,不确定它是否会影响这个)?

【讨论】:

这从未发生在我身上(据我所知),但我想确保我编写代码来处理空值(如果可能)。 @Xeoncross:我会说这是无效程序状态的某种迹象,并暗示可能进一步的状态损坏。不过,在野外不太可能遇到这种情况。【参考方案4】:

它不应为空,并且没有任何东西无法连接到您的网络服务。任何连接都必须有一个 IP 地址来发送和接收数据。该 IP 地址是否可信是另一回事。

【讨论】:

以上是关于REMOTE_ADDR 可能是空白的吗?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 $_SERVER["REMOTE_ADDR"] 显示的 IP 与我的外部 IP 不同? [复制]

如何伪造 $_SERVER['REMOTE_ADDR'] 变量?

负载均衡器 $_SERVER['REMOTE_ADDR'] 不工作

Nginx proxy_pass 与 $remote_addr

PHP 中的 REMOTE_ADDR 和 IPv6

nginx禁止ip默认参数是$remote_addr无法禁止真实ip的问题