在 Heroku 上获取客户端的真实 IP 地址
Posted
技术标签:
【中文标题】在 Heroku 上获取客户端的真实 IP 地址【英文标题】:Get client's real IP address on Heroku 【发布时间】:2013-08-18 07:52:38 【问题描述】:在任何 Heroku 堆栈上,我想获取客户端的 IP。我的第一次尝试可能是:
request.headers['REMOTE_ADDR']
这当然行不通,因为所有请求都通过代理传递。所以替代方案是使用:
request.headers['X-Forwarded-For']
但这不太安全,是吗?
如果它只包含一个值,我接受这个。如果它包含多个值(逗号分隔),我可以取第一个。
但是如果有人操纵这个值怎么办?我不能像对request.headers['REMOTE_ADDR']
那样信任request.headers['X-Forwarded-For']
。而且也没有我可以使用的受信任代理列表。
但必须有某种方式来可靠地获取客户端的 IP 地址,始终如此。你认识一个吗?
在their docs 中,Heroku 将X-Forwarded-For
描述为“连接到 Heroku 路由器的客户端的原始 IP 地址”。
这听起来好像 Heroku 可以用原始远程 IP 覆盖 X-Forwarded-For
。这样可以防止欺骗,对吗?有人可以验证吗?
【问题讨论】:
对不起,这是什么语言?如果不是python,我如何在python中做到这一点? Heroku docs(与您提到的相同)出于安全原因明确表示不信任X-Forwarded-For
标头。自 2013 年以来一定有更新。
原始问题在ENV['REMOTE_ADDR']
与ENV['HTTP_X_FORWARDED_FOR']
的上下文中提出这个问题;修改问题以指定这些是请求标头 (request.headers
)
【参考方案1】:
来自当时 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"
顺便说一下,这与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'];
【讨论】:
devcenter.heroku.com/articles/http-routing 没有像您的帖子那样详细,但对于想要检查行为是否发生变化的人来说可能会很有趣。这里的好处是 Rails RemoteIp 中间件应该可以正常工作,返回最后一个可信任的 IP,这将是客户端或它们经过的任何不受信任的代理,而无需配置任何 Heroku 代理地址。 我不认为你给出的例子与***所说的相反(至少现在)。客户端使用标头中的 8.8.8.8 连接到 heroku,本质上假装它是转发来自 8.8.8.8 的请求的代理,然后 heroku 将它接收到的连接的 IP 附加到列表中。或者在他们的列表示例中,正常请求将是 [client],而伪造的请求将是 [fakeclient, client]。没有插入代理 IP,因为 Heroku 只使用 1 个代理。 @nruth 我的意思是 Wikipedia 说“最左边的是原始客户端”,而 Heroku 说“真正的起源将永远是列表中的最后一项”。 两者都是正确的。 “真实来源”应该是最左边的 IP,但它可以被欺骗。 Heroku 只保证 最右边的 IP,因为那个 IP 连接到 Heroku,但它可能是代理。有关更多信息,请参阅 Joel Watson 的详细回答。 @wuputah 我认为你是正确的。我正在测试这个,我看到的是最左边的地址是我自己的 IP,而 Heroku 添加了第二个地址......但这不是我的 IP......所以对于最初的问题,您似乎实际上需要 数组中的第一个 元素(例如用于 GeoIP 目的),而不是最后一个。【参考方案2】:我在 Heroku 的支持部门工作,并花了一些时间与我们的路由工程师讨论这个问题。我想发布一些额外的信息来澄清这里发生的一些事情。
上面答案中提供的示例只是巧合地最后显示了客户端 IP,这并不能真正保证。它不是第一个的原因是因为发起请求声称它正在转发X-Forwarded-For
标头中指定的 IP。当 Heroku 路由器收到请求时,它只是将直接连接到X-Forwarded-For
列表的 IP 附加到已注入请求的 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。
【讨论】:
我一直在跟进,直到最后一段。这是否应该说 IP 链应该从右到左读取,并且客户端 IP 应该是最右边的 IP? 客户端IP是最左边的IP。约定是在遇到其他 IP 时将其附加到列表中,因此在大多数情况下,列表中的第一个 IP 应该是您想要的实际客户端 IP。列表中的其他 IP 均为中间 IP。请记住,列表中的 IP 可以在请求链中的任何位置任意修改,因此您不能保证该 IP 是正确的。 那么在上面的例子中,12.12.12.12
会被认为是客户端的 IP,即使它是 9.9.9.9
,因为他们故意将额外的 IP 添加到标头?
我可以在测试后验证客户端 IP 是第一个而不是最后一个 - 很高兴我在部署之前进行了测试 =p【参考方案3】:
您永远无法真正相信来自客户的任何信息。这更多的是一个你信任谁以及如何验证它的问题。如果他们的代码中有错误,或者他们以某种方式被黑客入侵,即使 Heroku 也可能会受到影响以提供错误的HTTP_X_FORWARDED_FOR
值。另一种选择是其他一些 Heroku 机器在内部连接到您的服务器并完全绕过它们的代理,同时伪造 REMOTE_ADDR
和/或 HTTP_X_FORWARDED_FOR
。
这里的最佳答案取决于您要做什么。如果您尝试验证您的客户端,客户端证书可能是更合适的解决方案。如果您需要 IP 的只是地理位置,那么信任输入可能就足够了。最坏的情况是,有人会伪造位置并获取错误的内容...如果您有不同的用例,在这两个极端之间还有许多其他解决方案。
【讨论】:
谢谢!我只是问,因为 Heroku 知道 客户的真实 IP。这只是 Heroku 的代理收到请求的 IP。如果 that 又是一个代理,我们不必关心。当客户端位于代理后面时,REMOTE_ADDR
也会出现这种情况。 Heroku 被黑客入侵或代码中存在错误是一个例外,我们也不应该在意。因为我们对此无能为力。但是,如果 Heroku 只是覆盖 HTTP_X_FORWARDED_FOR
,我们将始终知道客户端的真实 IP,我们通常会通过 REMOTE_ADDR
获得。对吗?
是的,没错。 Wikipedia 上提供了更多信息。以上是关于在 Heroku 上获取客户端的真实 IP 地址的主要内容,如果未能解决你的问题,请参考以下文章