Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/230.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php nginx能否处理重复的X-Forwarded-For头?_Php_Nginx - Fatal编程技术网

Php nginx能否处理重复的X-Forwarded-For头?

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

当用户使用代理(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
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将选择最左边的IP
123.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级别处理这种情况,因为您将永远无法看到原始标题

可能的解决方案

  • 将apache放在Nginx前面。让nginx在unix套接字上侦听,并使用apache将请求反向代理到nginx
  • 用Apache替换Nginx
  • 创建一个Nginx插件来合并标题。以下两个项目应该给你一个良好的开端
  • TL;博士
    • nginx:
      fastcgi_param HTTP_MERGED_X_FORWARDED_为$HTTP_X_FORWARDED_
    • php:
      $\u服务器['HTTP\u合并的\u X\u转发的\u']
    解释 您可以使用访问所有http头。当使用这个变量时,nginx甚至会为您进行头合并

    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相反,它可以正确地连接多个传入的头。)因此,是的,我想我只是在路上的某个地方自己发现了它。不过,我有一个预感。