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

时间:2013-08-16 01:12:41

标签: http heroku ip-address

在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。这样可以防止欺骗,对吧?有人可以验证这个吗?

3 个答案:

答案 0 :(得分:33)

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

  

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

这意味着,如果您以正常方式访问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"
}

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

答案 1 :(得分: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。

答案 2 :(得分:3)

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

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