在Heroku上获取客户端的真实IP地址

问题描述 投票:24回答:3

在Heroku Cedar上,我想获得客户端的IP。第一次尝试:

ENV['REMOTE_ADDR']

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

ENV['HTTP_X_FORWARDED_FOR']

但这不太安全,是吗?

如果它只包含一个值,我就拿这个。如果它包含多个值(以逗号分隔),我可以选择第一个值。

但是,如果有人操纵这个价值呢?我不能像ENV['HTTP_X_FORWARDED_FOR']那样信任ENV['REMOTE_ADDR']。并且我也没有可以使用的可信代理列表。

但总是必须有一些方法来可靠地获取客户端的IP地址。你知道吗?

their docs中,Heroku描述X-Forwarded-For是“连接到Heroku路由器的客户端的原始IP地址”。

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

http heroku ip-address
3个回答
33
投票

来自Heroku当时的安全总监雅各布:

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

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

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

如果你试图欺骗知识产权,你的所谓来源就会被反映出来,但是 - 关键 - 你的真实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"
}

顺便说一句,这与what is described on 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'];
    }
}

24
投票

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

上面答案中提供的示例只是巧妙地显示了客户端IP,并且不能保证。它不是第一个的原因是因为原始请求声称它正在转发X-Forwarded-For头中指定的IP。当Heroku路由器收到请求时,它只是在注入请求之后附加了直接连接到X-Forwarded-For列表的IP。我们的路由器始终将连接到我们平台前面的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。


3
投票

您永远不会真正信任来自客户端的任何信息。这更像是一个问题,你信任谁,你如何验证它。即使Heroku可能会受到影响,如果他们的代码中有错误,或者他们会以某种方式被黑客攻击,提供一个糟糕的HTTP_X_FORWARDED_FOR值。另一种选择是其他一些Heroku机器在内部连接到您的服务器并且在伪造REMOTE_ADDR和/或HTTP_X_FORWARDED_FOR时完全绕过它们的代理。

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

© www.soinside.com 2019 - 2024. All rights reserved.