Php nginx能否处理重复的X-Forwarded-For头?
当用户使用代理(Google data saver等)时,浏览器会向服务器添加X-Forwarded-For客户端的真实ip地址。我们的负载平衡器将所有标头+客户端的ip地址作为X-Forwarded-For标头传递给nginx服务器。请求头的示例:Php nginx能否处理重复的X-Forwarded-For头?,php,nginx,Php,Nginx,当用户使用代理(Google data saver等)时,浏览器会向服务器添加X-Forwarded-For客户端的真实ip地址。我们的负载平衡器将所有标头+客户端的ip地址作为X-Forwarded-For标头传递给nginx服务器。请求头的示例: X-Forwarded-For: 1.2.3.4 X-Forwarded-Port: 80 X-Forwarded-Proto: http Host: *.*.*.* Accept-Encoding: gzip, deflate, sdch Acc
X-Forwarded-For: 1.2.3.4
X-Forwarded-Port: 80
X-Forwarded-Proto: http
Host: *.*.*.*
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8,tr;q=0.6
Save-Data: on
Scheme: http
Via: 1.1 Chrome-Compression-Proxy
X-Forwarded-For: 1.2.3.5
Connection: Keep-alive
有没有办法将这两个X-Forwarded-For头分别传递给php?您可以在变量
$realip\u remote\u addr
中获得连接ELB的原始客户端地址,但请注意,该变量仅添加到nginx 1.9.7中,因此您需要运行最新版本的nginx
更多信息
例如,使用此配置:
set_real_ip_from 127.0.0.1;
set_real_ip_from 192.168.2.1;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
以及一个X-Forwarded-For
报头,导致:
X-Forwarded-For: 123.123.123.123, 192.168.2.1, 127.0.0.1
默认情况下,nginx将选择最左边的IP123.123.123
作为客户端的IP地址,而不是受信任的代理
但是,
$realip\u remote\u addr
保留原始客户端地址X-Forwarded-for的头应该由请求的每个代理内联追加。你不应该得到两个标题。由于这些值是通过设计附加的,任何人都可以将ip添加到该列表中,因此不要将其用于安全检查。如果需要检查ip的安全性,请在web服务器上设置X-Real-ip头,覆盖任何传入的值 您要查找的内容需要在web服务器级别处理。为此,我创建了两台服务器,一台使用apache,另一台使用nginx。测试命令
curl -H "X: Y" -H "X: Z" http://localhost:8088/router.php | jq
Apache
使用apache执行时,输出如下所示
{
"HEADERS": {
"Host": "localhost:8088",
"User-Agent": "curl/7.47.0",
"Accept": "*/*",
"X": "Y, Z"
}
}
如您所见,我们向apache传递了两个头,apache使用,
将它们组合起来。如果我们将第一个标题更改为已经包含,
它仍然可以正常工作
$ curl -H "X: Y, A" -H "X: Z" http://localhost:8088/router.php | jq
{
"HEADERS": {
"Host": "localhost:8088",
"User-Agent": "curl/7.47.0",
"Accept": "*/*",
"X": "Y, A, Z"
}
}
Nginx
现在,nginx上的相同请求产生了
{
"HEADERS": {
"X": "Z",
"Accept": "*/*",
"User-Agent": "curl/7.47.0",
"Host": "localhost"
}
}
现在并不是说Nginx没有将这些头发送到PHP-FPM,而是按原样发送。PHP-FPM不会将这些重复的头合并为一个。因此,在脚本中,您只能获得最新的头
编辑-1:使用fastcgi_参数合并
感谢@AronCederholm指出,通过指定FASTCGI_PARAM,合并确实有效
我最初测试了相同的方法,但结果是出现了空白标题。我试着加上
fastcgi_param X-Forwarded-For $http_x_forwarder_for;
刚才在读了他的信息后,我意识到我的配置中有一个输入错误。应该是的
fastcgi_param X-Forwarded-For $http_x_forwarded_for;
在这一变化之后,收割台工作正常。但是它不会出现在getallheaders()中。它将通过$\u服务器[]
提供,如下面的响应所示
$ curl -v -H 'X-Forwarded-For: 127.0.0.1' -H 'X-Forwarded-For: 8.8.8.8' http://localhost/router.php | jq
{
"HEADERS": {
"X-Forwarded-For": "8.8.8.8",
"Accept": "*/*",
"User-Agent": "curl/7.47.0",
"Host": "localhost"
},
"SERVER": {
"USER": "vagrant",
"HOME": "/home/vagrant",
"HTTP_X_FORWARDED_FOR": "8.8.8.8",
"HTTP_ACCEPT": "*/*",
"HTTP_USER_AGENT": "curl/7.47.0",
"HTTP_HOST": "localhost",
"X-Forwarded-For": "127.0.0.1, 8.8.8.8",
原始答案
不幸的是,我没有找到Nginx或PHP-FPM的设置或插件,这些设置或插件允许您将重复的头合并为一个。您无法在PHP级别处理这种情况,因为您将永远无法看到原始标题
可能的解决方案
- nginx:
fastcgi_param HTTP_MERGED_X_FORWARDED_为$HTTP_X_FORWARDED_
- php:
$\u服务器['HTTP\u合并的\u X\u转发的\u']
CustomHeader: foo
CustomHeader: bar
将转换为以下值:
foo, bar
因此,您所需要做的就是将这个变量传递给php
概念证明:
在nginx服务器块中:
location ~ \.php$ {
fastcgi_pass unix:run/php/php5.6-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTP_MERGED_X_FORWARDED_FOR $http_x_forwarded_for;
include fastcgi_params;
}
curl -v -H 'X-Forwarded-For: 127.0.0.1' -H 'X-Forwarded-For: 8.8.8.8' http://localhost/test.php
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET /test.php HTTP/1.1
> Host: localhost
> User-Agent: curl/7.47.0
> X-Forwarded-For: 127.0.0.1
> X-Forwarded-For: 8.8.8.8
>
< HTTP/1.1 200 OK
< Server: nginx/1.10.3 (Ubuntu)
< Date: Wed, 01 Nov 2017 09:07:51 GMT
< Content-Type: text/html; charset=UTF-8
< Transfer-Encoding: chunked
< Connection: keep-alive
<
* Connection #0 to host localhost left intact
127.0.0.1, 8.8.8.8
test.php
<?php
die($_SERVER['HTTP_MERGED_X_FORWARDED_FOR']);
给出以下响应:
location ~ \.php$ {
fastcgi_pass unix:run/php/php5.6-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTP_MERGED_X_FORWARDED_FOR $http_x_forwarded_for;
include fastcgi_params;
}
curl -v -H 'X-Forwarded-For: 127.0.0.1' -H 'X-Forwarded-For: 8.8.8.8' http://localhost/test.php
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET /test.php HTTP/1.1
> Host: localhost
> User-Agent: curl/7.47.0
> X-Forwarded-For: 127.0.0.1
> X-Forwarded-For: 8.8.8.8
>
< HTTP/1.1 200 OK
< Server: nginx/1.10.3 (Ubuntu)
< Date: Wed, 01 Nov 2017 09:07:51 GMT
< Content-Type: text/html; charset=UTF-8
< Transfer-Encoding: chunked
< Connection: keep-alive
<
* Connection #0 to host localhost left intact
127.0.0.1, 8.8.8.8
*正在尝试127.0.0.1。。。
*已连接到本地主机(127.0.0.1)端口80(#0)
>GET/test.php HTTP/1.1
>主机:本地主机
>用户代理:curl/7.47.0
>X-For:127.0.0.1
>X-For:8.8.8.8
>
轰!这样,您就可以访问所有X-FORWARDED-FOR
头,作为$\u服务器['HTTP\u MERGED\u X\u FORWARDED\u']中的逗号分隔字符串。
当然,您可以使用您想要的任何名称,而不仅仅是
HTTP\u MERGED\u X\u FORWARDED\u FOR
iirc第二个名称会覆盖第一个名称。您的负载平衡器应该执行X-Forwarded-For:1.2.3.4,1.2.3.5
它不能,它是托管服务,所以我不能更改任何内容您当前在PHP脚本中得到了什么?nginx肯定可以处理这个问题,但不清楚你在说什么样的设置。如果有很多,你能确认它合并了real\u ip\u头吗?@MatTheCat不能确认。实际上,nginx为你合并头。请参阅如何触发此操作。@AronCederholm,谢谢您的指点。再次挖掘之后,我意识到我的研究哪里出了问题。我在测试同一个解决方案时出现了输入错误,我认为nginx没有处理这种情况,而是在重复检查我的configNice!不知道nginx合并头文件您是在某处(哪里?)找到它的还是自己发现的?据我所知,它没有正式的文档记录,但该行为得到了的支持,而且,还暗示这是和中最受欢迎的行为(这与$proxy_add_x_forwarded_for相反,它可以正确地连接多个传入的头。)因此,是的,我想我只是在路上的某个地方自己发现了它。不过,我有一个预感。