nginx可以处理重复的X-Forwarded-For标头吗?

时间:2016-08-22 11:32:49

标签: php nginx

当用户使用代理(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?

4 个答案:

答案 0 :(得分:6)

TL; DR

  • nginx:fastcgi_param HTTP_MERGED_X_FORWARDED_FOR $http_x_forwarded_for
  • php:$_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

概念证明:

你的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;
}

<强> 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级别处理这种情况,因为您将永远无法看到原始标头。

可能的解决方案

  1. 将apache放在Nginx面前。让nginx监听unix套接字并使用apache将请求反向代理到nginx
  2. 用Apache替换Nginx
  3. 创建一个Nginx插件来合并标头。以下两个项目应该为您提供一个良好的开端
  4. https://github.com/giom/nginx_accept_language_module

    https://github.com/openresty/headers-more-nginx-module

答案 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标头,覆盖任何传入的值。