如何正确使用HTTP_X_FORWARDED_FOR?

kin*_*ple 35 php ip proxy forwarding

好吧,我有一个小的身份验证问题.我的Web服务允许使用用户名和密码通过HTTP连接到我的API,但此连接也可以限制为特定的IP地址.

这意味着$_SERVER['REMOTE_ADDR']可能不正确.我已经知道任何IP信息都无法真正依赖 - 我只有在尝试添加另一层安全性时才有限制.

如果这是对我的Web服务器的请求的一般概述:

clientSERVER => clientPROXY => myPROXY => mySERVER

那么这意味着mySERVER显示REMOTE_ADDRmyPROXY而不是客户端的myPERVX,并将客户端的实际IP发送为HTTP_X_FORWARDED_FOR.

为了解决这个问题,我的Web服务有一个"可信代理"IP地址列表,如果REMOTE_ADDR是来自其中一个可信IP地址,那么它告诉我的Web服务实际的IP地址是值HTTP_X_FORWARDED_FOR.

现在问题出在clientPROXY上.这意味着(通常)mySERVER获取HTTP_X_FORWARDED_FOR具有多个IP地址的值.根据HTTP_X_FORWARDED_FOR文档,该值是以逗号分隔的IP地址列表,其中第一个IP是实际真实客户端的IP,而每个其他IP地址是代理的IP地址.

因此,如果HTTP_X_FORWARDED_FOR有多个值并且我的服务是IP限制的,我是否必须检查HTTP_X_FORWARDED_FOR针对我允许的IP列表的"最后"值并忽略实际的客户端IP?

我假设在一个系统中,我必须设置允许的IP地址列表,白名单IP地址应该是代理的IP地址而不是代理后面的IP(因为那可能是一些本地主机IP并经常更改) .

那是什么HTTP_CLIENT_IP

Hri*_*hra 26

您可以使用此功能获取正确的客户端IP:

public function getClientIP(){       
     if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)){
            return  $_SERVER["HTTP_X_FORWARDED_FOR"];  
     }else if (array_key_exists('REMOTE_ADDR', $_SERVER)) { 
            return $_SERVER["REMOTE_ADDR"]; 
     }else if (array_key_exists('HTTP_CLIENT_IP', $_SERVER)) {
            return $_SERVER["HTTP_CLIENT_IP"]; 
     } 

     return '';
}
Run Code Online (Sandbox Code Playgroud)

  • 这个答案没有解决问题中描述的具体情况,其中**并非所有请求都通过代理**传递.因此,直接从客户端收到的请求可能在标头中包含不准确的IP地址. (5认同)
  • 这是一个可能的安全漏洞。任何人都可以将 X-Forwarded-For 标头添加到他们的请求中。 (4认同)

use*_*828 21

我喜欢Hrishikesh的答案,我只想添加它...因为我们看到在使用多个代理时遇到逗号分隔的字符串,我们发现有必要添加一个爆炸并获取最终值,如这个:

$IParray=array_values(array_filter(explode(',',$_SERVER['HTTP_X_FORWARDED_FOR'])));
return end($IParray);
Run Code Online (Sandbox Code Playgroud)

array_filter在那里删除空条目.

  • 请注意,似乎使用列表中的最后一个值仍然可能使用代理的IP.根据以下链接,始发客户端是第一个IP.http://en.wikipedia.org/wiki/X-Forwarded-For (3认同)
  • 另请注意,同一来源说这很容易伪造,所以最后一个更可靠.因此每个用例可能会做出不同的选择.如果获取IP的用例是打击欺诈或垃圾邮件,则第一个IP可能毫无意义,最可靠的地址 - 最后一个 - 最有用.如果获取IP的用例不那么邪恶的活动,第一个将是最有用的. (2认同)

Erk*_*i A 13

鉴于最新的httpoxy漏洞,确实需要一个完整的示例,如何HTTP_X_FORWARDED_FOR正确使用.

所以这里有一个用PHP编写的例子,如何检测客户端IP地址,如果你知道客户端可能在代理后面,你知道这个代理可以信任.如果您不知道任何受信任的代理,请使用REMOTE_ADDR

<?php

function get_client_ip ()
{
    // Nothing to do without any reliable information
    if (!isset ($_SERVER['REMOTE_ADDR'])) {
        return NULL;
    }

    // Header that is used by the trusted proxy to refer to
    // the original IP
    $proxy_header = "HTTP_X_FORWARDED_FOR";

    // List of all the proxies that are known to handle 'proxy_header'
    // in known, safe manner
    $trusted_proxies = array ("2001:db8::1", "192.168.50.1");

    if (in_array ($_SERVER['REMOTE_ADDR'], $trusted_proxies)) {

        // Get the IP address of the client behind trusted proxy
        if (array_key_exists ($proxy_header, $_SERVER)) {

            // Header can contain multiple IP-s of proxies that are passed through.
            // Only the IP added by the last proxy (last IP in the list) can be trusted.
            $proxy_list = explode (",", $_SERVER[$proxy_header]);
            $client_ip = trim (end ($proxy_list));

            // Validate just in case
            if (filter_var ($client_ip, FILTER_VALIDATE_IP)) {
                return $client_ip;
            } else {
                // Validation failed - beat the guy who configured the proxy or
                // the guy who created the trusted proxy list?
                // TODO: some error handling to notify about the need of punishment
            }
        }
    }

    // In all other cases, REMOTE_ADDR is the ONLY IP we can trust.
    return $_SERVER['REMOTE_ADDR'];
}

print get_client_ip ();

?>
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的答案,但有一个小问题.如果启用严格错误报告,则尝试在一行上"修剪(结束(爆炸())"将返回"仅应通过引用传递变量".要解决这个问题,首先将展开的代理标​​头设置为变量,然后将"trim(end())"设置为. (2认同)
  • 我相信这是不正确的。根据 Mozilla 的说法,原始 IP 是第一个,而不是最后一个,所以它应该是 `trim(array_shift())` https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X -转发-为 (2认同)

The*_*ist -4

HTTP_CLIENT_IP 是获取用户 IP 地址的最可靠方法。接下来是 HTTP_X_FORWARDED_FOR,然后是 REMOTE_ADDR。按该顺序检查所有三个,假设设置的第一个(isset($_SERVER['HTTP_CLIENT_IP'])如果设置了该变量则返回 true)是正确的。您可以使用各种方法独立检查用户是否正在使用代理。看一下这个

  • 这些标头都不比其他标头“更可靠”。它们都可以由客户端伪造,并且您的应用程序需要知道什么是可信的,什么是不可信的:您通常知道您的基础设施以及 HTTP 服务器前面的任何代理/负载均衡器的 IP 地址。 (8认同)
  • 为什么关于这个话题有这么多虚假信息?`$_SERVER['REMOTE_ADDR']` 是*唯一*不受远程用户影响的可靠字段,所有其他字段都是从标头中解析的。 (7认同)