当用户使用代理(Google数据保护程序等)时,浏览器会为客户添加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
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?
答案 0 :(得分:6)
fastcgi_param HTTP_MERGED_X_FORWARDED_FOR $http_x_forwarded_for
$_SERVER['HTTP_MERGED_X_FORWARDED_FOR']
您可以使用the $http_<header_name>
variable访问所有http标头。当使用这个变量时,nginx甚至会为你做标题合并
CustomHeader: foo
CustomHeader: bar
转换为值:
foo, bar
因此,您需要做的就是将此变量传递给php with fastcgi_param
fastcgi_param HTTP_MERGED_X_FORWARDED_FOR $http_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;
}
<强> test.php的强>
<?php
die($_SERVER['HTTP_MERGED_X_FORWARDED_FOR']);
最后看看curl会发生什么:
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
轰!在那里,您可以访问所有X-FORWARDED-FOR
标头,作为$_SERVER['HTTP_MERGED_X_FORWARDED_FOR']
中逗号分隔的字符串
当然,您可以使用您想要的任何名称,而不仅仅是HTTP_MERGED_X_FORWARDED_FOR
。
答案 1 :(得分:1)
您需要在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_param合并
感谢@AronCederholm通过指定FASTCGI_PARAM指出合并确实有用
我最初测试了相同的方法,但它导致了空白标题。我曾尝试添加
fastcgi_param X-Forwarded-For $http_x_forwarder_for;
刚看完他的留言后,我意识到我的配置中有一个拼写错误。应该是
fastcgi_param X-Forwarded-For $http_x_forwarded_for;
在此更改之后,标题可以正常工作。但它不会出现在getallheaders()中。它将通过$_SERVER[]
提供,如下面的回复
$ 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级别处理这种情况,因为您将永远无法看到原始标头。
可能的解决方案
答案 2 :(得分:0)
您可以在变量$realip_remote_addr
中获取连接ELB的原始客户端地址,但请注意,此变量仅在nginx 1.9.7中添加,因此您需要运行一个非常好的最新版本的nginx。
了解更多信息。 ngx_http_realip_module variables
例如,使用此配置:
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会将最左边的IP 123.123.123.123
作为客户端的IP地址。
但$realip_remote_addr
保留原始客户地址
答案 3 :(得分:0)
X-Forwarded-For的标题应附加在您的请求内联的每个代理中。你不应该得到两个标题。因为值是由设计附加的,所以任何人都可以将ip添加到该列表中,因此不要将其用于安全检查。如果您需要检查IP是否安全,请在Web服务器上设置X-Real-IP标头,覆盖任何传入的值。