我在Heroku的支持部门工作,已经与我们的路由工程师讨论了一段时间。我想发布一些额外的信息,以澄清这里正在发生的事情。
上面回答中提供的示例仅仅是巧合地显示了客户端IP,并不真正保证。之所以它不是第一个,是因为起始请求声称它正在转发X-Forwarded-For
头部中指定的IP地址。当Heroku路由器收到请求时,它只是将直接连接到X-Forwarded-For
列表中的IP添加到已注入请求的IP后面。我们的路由器总是将连接到AWS ELB的IP添加到我们平台前面的列表中的最后一个IP。这个IP可能是原来的IP(在只有一个IP的情况下,几乎肯定是这样),但是一旦有多个IP链接的时候,所有赌注都可以打翻。惯例总是将链中最新的IP添加到列表末尾(这就是我们所做的),但在链的任何一点上,该链都可以被更改,不同的IP可能会被插入。因此,从我们平台的角度来看,唯一可靠的IP是列表中的最后一个IP。
为了说明这一点,假设有人发起请求,并任意添加了3个额外的IP到X-Forwarded-For
头部:
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呢?从Google的角度来看,无法确定。实际上,客户端IP是9.9.9.9。不过,最后一个列出的IP地址是2.2.2.2,而第一个地址是12.12.12.12。谷歌只知道2.2.2.2 IP地址是正确的,因为那是实际连接到他们服务的IP地址,但是他们无法从可用数据中确定是否这是请求的初始客户端。同样的,当这个头部只有一个IP地址时,那就是直接连接到我们服务的IP地址,所以我们知道它是可靠的。
从实际角度考虑,这个IP地址很可能在大多数情况下是可靠的(因为大多数人不会费心伪造IP地址)。不幸的是,无法防止这种类型的欺骗,当请求到达Heroku路由器时,我们无法确定X-Forwarded-For
链中的IP地址是否被篡改。
除了所有可靠性问题之外,这些IP链应始终从左至右读取。客户端IP 应该 总是最左边的IP地址。
X-Forwarded-For
头。自2013年以来必须进行更新。 - staplesENV['REMOTE_ADDR']
与ENV['HTTP_X_FORWARDED_FOR']
之间进行比较;修改问题以指定这些是请求头(request.headers
)。 - Jason FB