42

在任何 Heroku 堆栈上,我都想获取客户端的 IP。我的第一次尝试可能是:

request.headers['REMOTE_ADDR']

当然,这不起作用,因为所有请求都通过代理传递。所以替代方法是使用:

request.headers['X-Forwarded-For']

但这不太安全,是吗?

如果它只包含一个值,我接受这个。如果它包含多个值(逗号分隔),我可以取第一个。

但是如果有人操纵这个值呢?我不能尽request.headers['X-Forwarded-For']我所能相信request.headers['REMOTE_ADDR']。也没有我可以使用的受信任代理列表。

但是必须有某种方法可以始终可靠地获取客户端的 IP 地址。你认识一个吗?

他们的文档中,Heroku 将其描述X-Forwarded-For为“连接到 Heroku 路由器的客户端的原始 IP 地址”。

这听起来好像 Heroku 可以X-Forwarded-For用原始远程 IP 覆盖。这样可以防止欺骗,对吗?有人可以验证吗?

4

3 回答 3

55

来自 Heroku 当时的安全总监 Jacob:

路由器不会覆盖X-Forwarded-For,但它确实保证真正的来源始终是列表中的最后一项。

这意味着,如果您以正常方式访问 Heroku 应用程序,您只会在X-Forwarded-For标题中看到您的 IP 地址:

$ curl http://httpbin.org/ip
{
  "origin": "123.124.125.126",
}

如果您试图欺骗 IP,您所声称的来源就会被反映出来,但至关重要的是,您的真实 IP 也是如此。显然,这就是我们所需要的,所以有一个清晰而安全的解决方案可以在 Heroku 上获取客户端的 IP 地址:

$ curl -H"X-Forwarded-For: 8.8.8.8" http://httpbin.org/ip
{
  "origin": "8.8.8.8, 123.124.125.126"
}

顺便说一句,这与 Wikipedia 上的描述正好相反。

PHP实现:

function getIpAddress() {
    if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ipAddresses = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
        return trim(end($ipAddresses));
    }
    else {
        return $_SERVER['REMOTE_ADDR'];
    }
}
于 2013-08-29T17:38:01.257 回答
43

我在 Heroku 的支持部门工作,并花了一些时间与我们的路由工程师讨论这个问题。我想发布一些额外的信息来澄清一些关于这里发生的事情。

上面答案中提供的示例只是巧合地最后显示了客户端 IP,这并不能真正保证。它不是第一个的原因是因为发起请求声称它正在转发X-Forwarded-For标头中指定的 IP。当 Heroku 路由器收到请求时,它只是附加了直接连接到X-Forwarded-For在已注入请求的那一项之后列出。我们的路由器总是将连接到我们平台前面的 AWS ELB 的 IP 添加为列表中的最后一个 IP。这个 IP 可能是原始 IP(在只有一个 IP 的情况下,几乎可以肯定是),但是一旦有多个 IP 链接,所有的赌注都没有了。惯例总是将链中最新的 IP 添加到列表的末尾(这就是我们所做的),但在链上的任何点都可以更改该链并且可以插入不同的 IP。因此,唯一可靠的 IP(从我们平台的角度来看)是列表中的最后一个 IP。

为了说明,假设有人发起一个请求,并在 X-Forwarded-For 标头中任意添加了 3 个额外的 IP:

curl -H "X-Forwarded-For: 12.12.12.12,15.15.15.15,4.4.4.4" http://www.google.com

想象这台机器的 IP 是 9.9.9.9,它必须通过一个代理(例如,一所大学的校园代理)。假设代理的 IP 为 2.2.2.2。假设它没有配置为X-Forwarded-For去除标题(它可能不会),它只会将 9.9.9.9 IP 附加到列表的末尾并将请求传递给 Google。此时,标题将如下所示:

X-Forwarded-For: 12.12.12.12,15.15.15.15,4.4.4.4,9.9.9.9

然后,该请求将通过 Google 的端点,该端点将附加大学代理的 IP 2.2.2.2,因此标题在 Google 的日志中最终将如下所示:

X-Forwarded-For: 12.12.12.12,15.15.15.15,4.4.4.4,9.9.9.9,2.2.2.2

那么,哪个是客户端 IP?从谷歌的角度来看是不可能的。实际上,客户端 IP 是 9.9.9.9。列出的最后一个 IP 是 2.2.2.2,第一个是 12.12.12.12。Google 所知道的是 2.2.2.2 IP 绝对正确,因为那是实际连接到他们服务的 IP——但他们不知道这是否是请求的初始客户端,或者不是来自可用数据。同样,当这个头部只有一个 IP 时——即直接连接到我们服务的 IP,所以我们知道它是可靠的。

从实际的角度来看,这个 IP 在大多数时候很可能是可靠的(因为大多数人不会费心去欺骗他们的 IP)。不幸的是,不可能防止这种欺骗,当请求到达 Heroku 路由器时,我们无法判断X-Forwarded-For链中的 IP 是否被篡改。

撇开所有可靠性问题不谈,这些 IP 链应始终从左到右读取。客户端 IP始终是最左边的 IP。

于 2016-05-05T22:42:03.320 回答
4

你永远不能真正相信来自客户的任何信息。这更多的是一个你信任谁以及如何验证它的问题。HTTP_X_FORWARDED_FOR如果代码中存在错误,或者以某种方式被黑客入侵,即使 Heroku 也可能会受到影响而提供错误的值。另一种选择是其他一些 Heroku 机器在内部连接到您的服务器并在伪造REMOTE_ADDR和/或HTTP_X_FORWARDED_FOR.

这里的最佳答案取决于您要做什么。如果您尝试验证您的客户端,客户端证书可能是更合适的解决方案。如果您需要 IP 的只是地理位置,那么信任输入可能就足够了。最坏的情况是,有人会伪造位置并获取错误的内容……如果您有不同的用例,在这两个极端之间还有许多其他解决方案。

于 2013-08-16T01:45:20.503 回答