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

pvcm50d1  于 2022-11-24  发布在  其他
关注(0)|答案(4)|浏览(161)

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

request.headers['REMOTE_ADDR']

当然,这是行不通的,因为所有的请求都要通过代理传递用途:

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

但这不太安全,是吗?
如果它只包含一个值,我取这个值;如果它包含多个值(用逗号分隔),我可以取第一个值。
但是如果有人操纵了这个值呢?我不能像信任request.headers['REMOTE_ADDR']那样信任request.headers['X-Forwarded-For'],而且也没有我可以使用的可信代理列表。
但是必须有某种方法来可靠地获得客户端的IP地址,你知道吗?
在他们的文档中,Heroku描述了X-Forwarded-For是“连接到Heroku路由器的客户端的始发IP地址”。
这听起来好像Heroku可以用原始的远程IP覆盖X-Forwarded-For。这将防止欺骗,对吗?有人能验证这一点吗?

hc8w905p

hc8w905p1#

来自Jacob,Heroku当时的安全主管:
路由器不会覆盖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"
}

顺便说一句,这和维基百科上描述的正好相反。

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'];
    }
}
rxztt3cl

rxztt3cl2#

我在Heroku的支持部门工作,花了一些时间和我们的路由工程师讨论这个问题。我想发布一些额外的信息来澄清一些关于这里发生的事情。
在上面的例子中,客户端的IP地址碰巧显示在最后,这并不能保证。之所以没有显示在最前面,是因为发起请求的请求声称它正在转发X-Forwarded-For报头中指定的IP地址。当Heroku路由器收到请求时,它只是将直接连接到X-Forwarded-For列表的IP附加到已经注入到请求中的IP之后。我们的路由器总是将连接到AWS ELB的IP添加到我们平台前面,作为列表中的最后一个IP。此IP可能是原始IP(在只有一个IP的情况下,几乎可以肯定是这样),但是一旦有多个IP链接在一起,所有的赌注都泡汤了。(这是我们所做的),但是在链沿着任何一点,链可以被改变,不同的IP可以被插入。因此,唯一可靠的IP(从我们平台的Angular 来看)是列表中的最后一个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呢?从谷歌的Angular 来看,这是不可能的。实际上,客户端IP是9.9.9.9。最后列出的IP是2.2.2.2,第一个是12.12.12.12。谷歌只知道2.2.2.2 IP肯定是正确的,因为这是实际连接到他们服务的IP--但他们不知道。从可用的数据来看,我不知道这是否是请求的初始客户端。同样,当这个报头中只有一个IP时-即直接连接到我们服务的IP,因此我们知道它是可靠的。
从实际的Angular 来看,这个IP在 * 大多数时间 * 都是可靠的(因为大多数人不会费心去欺骗他们的IP)。不幸的是,我们不可能阻止这种欺骗,当请求到达Heroku路由器时,我们不可能知道X-Forwarded-For链中的IP是否被篡改了。
除了可靠性问题,这些IP链应该总是从左到右读取。客户端IP * 应该 * 总是最左边的IP。

ecfdbz9o

ecfdbz9o3#

你永远不可能真正信任来自客户端的任何信息。这更像是一个你信任谁以及你如何验证它的问题。即使是Heroku也可能受到影响,如果他们的代码中有bug,或者被黑客入侵。另一个选择是其他Heroku机器在内部连接到您的服务器,并绕过他们的代理,同时伪造REMOTE_ADDR和/或HTTP_X_FORWARDED_FOR
这里的最佳答案取决于您尝试做什么。如果您尝试验证您的客户端,客户端证书可能是一个更合适的解决方案。如果您需要的IP只是地理位置,信任输入可能就足够了。最坏的情况是,有人会伪造位置并获得错误的内容...如果您有不同的用例,在这两个极端之间存在许多其它解决方案。

pu3pd22g

pu3pd22g4#

如果我发出一个带有多个X-Forwarded-For头的请求:curl -s -v -H "X-Forwarded-For: 1.1.1.1, 1.1.1.2, 1.1.1.3" -H "X-Forwarded-For: 2.2.2.2" -H "X-Forwarded-For: 3.3.3.3" https://foo.herokuapp.com/

> X-Forwarded-For: 1.1.1.1, 1.1.1.2, 1.1.1.3
> X-Forwarded-For: 2.2.2.2
> X-Forwarded-For: 3.3.3.3

传递给应用程序的X-Forwarded-For标头将为:

1.1.1.1, 1.1.1.2, 1.1.1.3, <real client IP>, 2.2.2.2, 3.3.3.3

因此从该列表中选择最后一个并不成立:/

相关问题