PHP获取真实IP地址(代理检测)

7

如果用户使用代理发送真实IP的头部,我可以追踪到用户的“真实”IP。但是否有更好的解决方案或更多的头部信息?

由于此函数在脚本中经常使用,因此必须非常快速,但在这种情况下似乎不是很理想 :/

我想到了一些建议,但无法实现:

  • 按照“野外”中最常用的顺序放置标头,以便函数能够快速完成
  • 使IP的预匹配检测更快

===

function get_real_ip()
{
  $proxy_headers = array(
                          'CLIENT_IP', 
                          'FORWARDED', 
                          'FORWARDED_FOR', 
                          'FORWARDED_FOR_IP', 
                          'HTTP_CLIENT_IP', 
                          'HTTP_FORWARDED', 
                          'HTTP_FORWARDED_FOR', 
                          'HTTP_FORWARDED_FOR_IP', 
                          'HTTP_PC_REMOTE_ADDR', 
                          'HTTP_PROXY_CONNECTION',
                          'HTTP_VIA', 
                          'HTTP_X_FORWARDED', 
                          'HTTP_X_FORWARDED_FOR', 
                          'HTTP_X_FORWARDED_FOR_IP', 
                          'HTTP_X_IMFORWARDS', 
                          'HTTP_XROXY_CONNECTION', 
                          'VIA', 
                          'X_FORWARDED', 
                          'X_FORWARDED_FOR'
                         );

  foreach($proxy_headers as $proxy_header)
  {
    if(isset($_SERVER[$proxy_header]) && preg_match("/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/", $_SERVER[$proxy_header])) /* HEADER ist gesetzt und dies ist eine gültige IP */
    {
        return $_SERVER[$proxy_header];
    }
    else if(stristr(',', $_SERVER[$proxy_header]) !== FALSE) /* Behandle mehrere IPs in einer Anfrage(z.B.: X-Forwarded-For: client1, proxy1, proxy2) */
    {
      $proxy_header_temp = trim(array_shift(explode(',', $_SERVER[$proxy_header]))); /* Teile in einzelne IPs, gib die letzte zurück und entferne Leerzeichen */

      if(($pos_temp = stripos($proxy_header_temp, ':')) !== FALSE) $proxy_header_temp = substr($proxy_header_temp, 0, $pos_temp); /* Entferne den Port */

      if(preg_match("/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/", $proxy_header_temp) return $proxy_header_temp;
    }
  }

  return $_SERVER['REMOTE_ADDR'];
}

3
"ereg"已经被弃用。此外,您始终需要存储真实IP和“代理”IP,因为用户可以自行添加这些标头。 - ThiefMaster
谢谢,我用(可能更快的)preg_match替换了ereg,并添加了一个部分来检测字段中是否有更多的IP地址,例如:http://en.wikipedia.org/wiki/X-Forwarded-For - codex
1
我的真实IP地址是192.168.0.81。我想知道它有什么用处? - Your Common Sense
192.168.0.81地址有什么问题吗?为什么你认为在脚本输出中看不到它是不可能的?你有任何理由吗? - Your Common Sense
你明白所有这些花哨的HTTP头与TCP/IP协议无关吗?如果是这样,为什么你称其为“IP地址”,而不是“HTTP头”? - Your Common Sense
显示剩余12条评论
2个回答

2
如果代理服务器发送头文件,那么您可以获取客户端的原始IP。如果代理服务器没有发送头文件,则无法获取。 不幸的是(或者也许是幸运的,这取决于您的观点),就是这么简单。 在我们的内部网络中,我将"intranet.mydomain.com" 重定向到Web服务器上的"intranet",后者由于我们的内部网络/DNS配置而不使用代理...不知道您想做什么,但这可能会有用。 您还可以在浏览器中设置排除列表...

1
正则表达式验证对IPv6地址会失败,所以我宁愿删除它(或尝试找到更好的正则表达式)。
同时,stripos($proxy_header_temp, ':') 对于例如"::1"(本地主机,IPv6)会导致意外行为。
我的建议是进行上述修改:
function getIp()
{
    $proxy_headers = array(
        'CLIENT_IP',
        'FORWARDED',
        'FORWARDED_FOR',
        'FORWARDED_FOR_IP',
        'HTTP_CLIENT_IP',
        'HTTP_FORWARDED',
        'HTTP_FORWARDED_FOR',
        'HTTP_FORWARDED_FOR_IP',
        'HTTP_PC_REMOTE_ADDR',
        'HTTP_PROXY_CONNECTION',
        'HTTP_VIA',
        'HTTP_X_FORWARDED',
        'HTTP_X_FORWARDED_FOR',
        'HTTP_X_FORWARDED_FOR_IP',
        'HTTP_X_IMFORWARDS',
        'HTTP_XROXY_CONNECTION',
        'VIA',
        'X_FORWARDED',
        'X_FORWARDED_FOR'
    );
    $regEx = "/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/";
    foreach ($proxy_headers as $proxy_header) {
        if (isset($_SERVER[$proxy_header])) {
            /* HEADER ist gesetzt und dies ist eine gültige IP */
            return $_SERVER[$proxy_header];
        } else if (stristr(',', $_SERVER[$proxy_header]) !== false) {
            // Behandle mehrere IPs in einer Anfrage
            //(z.B.: X-Forwarded-For: client1, proxy1, proxy2)
            $proxy_header_temp = trim(
                array_shift(explode(',', $_SERVER[$proxy_header]))
            ); /* Teile in einzelne IPs, gib die letzte zurück und entferne Leerzeichen */

            // if IPv4 address remove port if exists
            if (preg_match($regEx, $proxy_header_temp)
                && ($pos_temp = stripos($proxy_header_temp, ':')) !== false
            ) {
                $proxy_header_temp = substr($proxy_header_temp, 0, $pos_temp);
            }
            return $proxy_header_temp;
        }
    }

    return $_SERVER['REMOTE_ADDR'];
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接