在任何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
。这将防止欺骗,对吗?有人能验证这一点吗?
4条答案
按热度按时间hc8w905p1#
来自Jacob,Heroku当时的安全主管:
路由器不会覆盖
X-Forwarded-For
,但它确实保证了真实的的源将始终是列表中的 * 最后 * 项。这意味着,如果您以正常方式访问Heroku应用,您将只在
X-Forwarded-For
头中看到您的IP地址:如果你试图伪造IP地址,你所谓的来源 * 会被 * 反映出来,但关键的是--你真实的的IP地址也会被反映出来。显然,这就是我们所需要的,所以有一个清晰而安全的解决方案可以在Heroku上获得客户的IP地址:
顺便说一句,这和维基百科上描述的正好相反。
PHP实作:
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:
假设这台机器的IP是9.9.9.9,并且它必须通过代理假设该代理的IP地址为2.2.2.2,并没有将其配置为剥离
X-Forwarded-For
报头(很可能不会),它只会把9.9.9.9 IP添加到列表的末尾,然后把请求传递给Google。然后,该请求将通过Google的端点,该端点将附加大学代理的IP地址2.2.2.2,因此,Google日志中的标题最终将如下所示:
那么,哪个是客户端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。
ecfdbz9o3#
你永远不可能真正信任来自客户端的任何信息。这更像是一个你信任谁以及你如何验证它的问题。即使是Heroku也可能受到影响,如果他们的代码中有bug,或者被黑客入侵。另一个选择是其他Heroku机器在内部连接到您的服务器,并绕过他们的代理,同时伪造
REMOTE_ADDR
和/或HTTP_X_FORWARDED_FOR
。这里的最佳答案取决于您尝试做什么。如果您尝试验证您的客户端,客户端证书可能是一个更合适的解决方案。如果您需要的IP只是地理位置,信任输入可能就足够了。最坏的情况是,有人会伪造位置并获得错误的内容...如果您有不同的用例,在这两个极端之间存在许多其它解决方案。
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
标头将为:因此从该列表中选择最后一个并不成立:/