在PHP中检索用户正确IP地址的最准确方法是什么?


301

我知道有很多$ _SERVER变量标头可用于IP地址检索。我想知道是否就使用上述变量最准确地检索用户的真实IP地址(众所周知没有一种方法是完美的)是否达成共识?

我花了一些时间试图找到一个深入的解决方案,并根据大量资源提出了以下代码。如果有人可以在答案中打个洞,或者对也许更准确的内容有所了解,我会喜欢的。

编辑包括来自@Alix的优化

 /**
  * Retrieves the best guess of the client's actual IP address.
  * Takes into account numerous HTTP proxy headers due to variations
  * in how different ISPs handle IP addresses in headers between hops.
  */
 public function get_ip_address() {
  // Check for shared internet/ISP IP
  if (!empty($_SERVER['HTTP_CLIENT_IP']) && $this->validate_ip($_SERVER['HTTP_CLIENT_IP']))
   return $_SERVER['HTTP_CLIENT_IP'];

  // Check for IPs passing through proxies
  if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
   // Check if multiple IP addresses exist in var
    $iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
    foreach ($iplist as $ip) {
     if ($this->validate_ip($ip))
      return $ip;
    }
   }
  }
  if (!empty($_SERVER['HTTP_X_FORWARDED']) && $this->validate_ip($_SERVER['HTTP_X_FORWARDED']))
   return $_SERVER['HTTP_X_FORWARDED'];
  if (!empty($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']) && $this->validate_ip($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']))
   return $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
  if (!empty($_SERVER['HTTP_FORWARDED_FOR']) && $this->validate_ip($_SERVER['HTTP_FORWARDED_FOR']))
   return $_SERVER['HTTP_FORWARDED_FOR'];
  if (!empty($_SERVER['HTTP_FORWARDED']) && $this->validate_ip($_SERVER['HTTP_FORWARDED']))
   return $_SERVER['HTTP_FORWARDED'];

  // Return unreliable IP address since all else failed
  return $_SERVER['REMOTE_ADDR'];
 }

 /**
  * Ensures an IP address is both a valid IP address and does not fall within
  * a private network range.
  *
  * @access public
  * @param string $ip
  */
 public function validate_ip($ip) {
     if (filter_var($ip, FILTER_VALIDATE_IP, 
                         FILTER_FLAG_IPV4 | 
                         FILTER_FLAG_IPV6 |
                         FILTER_FLAG_NO_PRIV_RANGE | 
                         FILTER_FLAG_NO_RES_RANGE) === false)
         return false;
     self::$ip = $ip;
     return true;
 }

警告语(更新)

REMOTE_ADDR仍然代表IP地址的最可靠来源。$_SERVER远程客户端可以很容易地欺骗这里提到的其他变量。该解决方案的目的是尝试确定位于代理后面的客户端的IP地址。为了您的一般目的,您可以考虑将其与直接从$_SERVER['REMOTE_ADDR']两者存储并返回的IP地址结合使用。

对于99.9%的用户,此解决方案将完全满足您的需求。它不会通过注入自己的请求标头来保护您免受0.1%希望滥用系统的恶意用户的侵害。如果依靠IP地址执行某些关键任务,请诉诸于REMOTE_ADDR代理代理,而不必理会这些代理。


2
对于whatismyip.com问题,我认为他们正在执行类似此脚本的操作,您是否在本地运行它?如果这就是为什么拥有内部IP的原因,那么在这种情况下,不会通过公用接口发送任何内容,因此没有任何有关php的信息
Matt

2
:确保你实施这个时候记住这一点stackoverflow.com/questions/1672827/...
凯文PENO

19
请记住,所有这些HTTP标头确实很容易修改:通过您的解决方案,我只需要配置浏览器以发送带有随机IP的X-Forwarded-For标头,您的脚本就会愉快地返回假地址。因此,根据您要尝试执行的操作,此解决方案的可靠性可能不如简单使用REMOTE_ADDR。
gnomnain 2010年

14
OMFG,“不可靠的IP”!我第一次在这里看到这样的废话。唯一可靠的IP地址是REMOTE_ADDR
您的常识2010年

3
-1这很容易受到欺骗。您要做的就是询问用户他的IP地址应该是什么。
rook

Answers:


268

这是获取IP地址的更短,更简洁的方法:

function get_ip_address(){
    foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key){
        if (array_key_exists($key, $_SERVER) === true){
            foreach (explode(',', $_SERVER[$key]) as $ip){
                $ip = trim($ip); // just to be safe

                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false){
                    return $ip;
                }
            }
        }
    }
}

希望对您有所帮助!


您的代码似乎已经很完整了,我看不到其中的任何可能的错误(除了常见的IP警告),validate_ip()尽管如此,我还是会更改功能以依赖过滤器扩展:

public function validate_ip($ip)
{
    if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false)
    {
        return false;
    }

    self::$ip = sprintf('%u', ip2long($ip)); // you seem to want this

    return true;
}

您的HTTP_X_FORWARDED_FOR代码段也可以从此简化:

// check for IPs passing through proxies
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
    // check if multiple ips exist in var
    if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',') !== false)
    {
        $iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);

        foreach ($iplist as $ip)
        {
            if ($this->validate_ip($ip))
                return $ip;
        }
    }

    else
    {
        if ($this->validate_ip($_SERVER['HTTP_X_FORWARDED_FOR']))
            return $_SERVER['HTTP_X_FORWARDED_FOR'];
    }
}

对此:

// check for IPs passing through proxies
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
    $iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);

    foreach ($iplist as $ip)
    {
        if ($this->validate_ip($ip))
            return $ip;
    }
}

您可能还需要验证IPv6地址。


4
我肯定会喜欢此filter_var修复程序,因为它消除了IP地址上的一些黑字未签名的int检查。我也喜欢这样的事实,它也使我可以选择验证IPv6地址。该HTTP_X_FORWARDED_FOR优化也大加赞赏。几分钟后,我将更新代码。
Corey Ballou 2010年

33
-1这很容易欺骗,您正在做的是询问用户他的IP地址应该是什么。
rook

7
@Rook:是的,我知道。OP意识到了这一点,我在回答中也提到了它。但感谢您的评论。
Alix Axel

1
仅供参考:我必须删除FILTER_FLAG_IPV6才能使Alix Axel的代码正常工作。
darkAsPitch

2
@ rubenrp81 TCP套接字处理程序是唯一的规范来源,其他所有内容都由攻击者控制。上面的代码是攻击者的梦想。
rook

12

即使这样,获取用户的真实IP地址也不可靠。他们所需要做的就是使用一个匿名代理服务器(一个不支持http_x_forwarded_for,http_forwarded等标头的服务器),而您所获得的只是他们的代理服务器的IP地址。

然后,您可以查看是否有匿名的代理服务器IP地址列表,但无法确定它也是100%准确的,并且最想做的就是让您知道它是代理服务器。而且,如果有人很聪明,他们可以欺骗标头进行HTTP转发。

假设我不喜欢当地的大学。我弄清楚他们注册了哪些IP地址,并通过做坏事使他们的IP地址在您的站点上被禁止,因为我弄清楚您尊重HTTP转发。列表是无止境的。

正如您所猜测的那样,然后是内部IP地址,例如我之前提到的大学网络。很多使用10.xxx格式。因此,您只知道将其转发到共享网络。

然后,我将不做太多介绍,但是动态IP地址已成为宽带的方式。所以。即使您获得了用户IP地址,也希望最长在2-3个月内更改一次。


感谢您的输入。我目前正在利用用户的IP地址,通过使用其C类IP作为限制会话劫持的限制因素来帮助进行会话身份验证,但允许在合理范围内使用动态IP。欺骗性IP和匿名代理服务器只是我要针对特定​​人群的处理内容。
科里·巴拉

@cballou-为此,肯定可以使用REMOTE_ADDR。任何依赖HTTP标头的方法都容易遭受标头欺骗。一个疗程多长时间?动态IP不会快速变化。
MZB

它们确实如此,尤其是如果我希望它们(更改许多驱动程序确实支持的mac地址)。只需REMOTE_ADDR本身就足以获取与其对话的最后一个服务器。因此,在代理情况下,您将获得代理IP。

8

我们用:

/**
 * Get the customer's IP address.
 *
 * @return string
 */
public function getIpAddress() {
    if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
        return $_SERVER['HTTP_CLIENT_IP'];
    } else if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
        return trim($ips[count($ips) - 1]);
    } else {
        return $_SERVER['REMOTE_ADDR'];
    }
}

HTTP_X_FORWARDED_FOR的爆炸是由于使用Squid时检测到IP地址的奇怪问题。


糟糕,我刚刚意识到您在展开时基本上会执行相同的操作,依此类推。再加上一点额外的东西。因此,我怀疑我的回答是否会有所帮助。:)
gabrielk 2010年

这将返回本地主机的地址
Scarl

3

我的答案基本上只是@AlixAxel答案的完整,经过完全验证和完整包装的版本:

<?php

/* Get the 'best known' client IP. */

if (!function_exists('getClientIP'))
    {
        function getClientIP()
            {
                if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) 
                    {
                        $_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
                    };

                foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key)
                    {
                        if (array_key_exists($key, $_SERVER)) 
                            {
                                foreach (explode(',', $_SERVER[$key]) as $ip)
                                    {
                                        $ip = trim($ip);

                                        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false)
                                            {
                                                return $ip;
                                            };
                                    };
                            };
                    };

                return false;
            };
    };

$best_known_ip = getClientIP();

if(!empty($best_known_ip))
    {
        $ip = $clients_ip = $client_ip = $client_IP = $best_known_ip;
    }
else
    {
        $ip = $clients_ip = $client_ip = $client_IP = $best_known_ip = '';
    };

?>

变化:

  • 它简化了函数名称(使用“ camelCase”格式样式)。

  • 它包括一项检查,以确保该功能尚未在代码的另一部分中声明。

  • 它考虑了“ CloudFlare”的兼容性。

  • 它将多个“与IP相关的”变量名初始化为'getClientIP'函数的返回值。

  • 它可以确保如果函数未返回有效的IP地址,则所有变量均设置为空字符串,而不是null

  • 只有(45)行代码。


2

最大的问题是出于什么目的?

您的代码几乎是尽可能全面的-但我看到,如果您发现看起来像是代理添加的标头,则可以使用CLIENT_IP的INSTEAD,但是如果您希望将此信息用于审核目的,则将其警告-非常简单假冒。

当然,您绝对不应将IP地址用于任何形式的身份验证-即使这些地址都可能被欺骗。

通过推出通过非HTTP端口连接回服务器的Flash或Java Applet,可以更好地测量客户端ip地址(这将显示透明代理或代理注入标头为false的情况-但是请记住,如果客户端只能通过Web代理进行连接,或者出站端口被阻止,则该applet将没有连接。

C。


考虑到我正在寻找仅PHP的解决方案,您是否建议我将$_SERVER['CLIENT_IP']if语句作为第二个添加?
Corey Ballou 2010年

否-只是如果您想对返回的数据有任何意义,那么保留网络端点地址(客户端IP)以及任何在代理添加标头中建议不同值的内容(例如,您可以看到很多192.168.1.x地址,但来自不同的客户端ips)C.
symcbean 2010年

1

我意识到上面有更好,更简洁的答案,这既不是功能,也不是最优美的脚本。在我们的例子中,我们需要以一种简单的开关来输出可欺骗的x_forwarded_for和更可靠的remote_addr。它需要允许将空格插入到if-none或if-singular(而不是仅返回预格式化的函数)中。它需要一个“ on or off”变量,并带有针对平台设置的每个开关的自定义标签。它还需要一种方法,使$ ip可以根据请求动态变化,以使其采用forwarded_for的形式。

我也没有看到任何人解决isset()vs!empty()-可能为x_forwarded_for不输入任何内容,但仍然触发isset()真值导致空白var,一种解决方法是使用&&并将两者组合为条件。请记住,您可以以x_forwarded_for的形式欺骗“ PWNED”之类的单词,因此,如果您的输出受到保护或进入DB,请确保将其灭菌为真正的ip语法。

另外,您可以使用Google翻译测试是否需要多重代理才能查看x_forwarder_for中的数组。如果您想欺骗标头进行测试,请查看此Chrome客户端标头欺骗扩展。在匿名代理后面,这将默认为仅标准remote_addr。

我不知道remote_addr可以为空的任何情况,但是为了以防万一,它作为备用。

// proxybuster - attempts to un-hide originating IP if [reverse]proxy provides methods to do so
  $enableProxyBust = true;

if (($enableProxyBust == true) && (isset($_SERVER['REMOTE_ADDR'])) && (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) && (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))) {
    $ip = end(array_values(array_filter(explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']))));
    $ipProxy = $_SERVER['REMOTE_ADDR'];
    $ipProxy_label = ' behind proxy ';
} elseif (($enableProxyBust == true) && (isset($_SERVER['REMOTE_ADDR']))) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $ipProxy = '';
    $ipProxy_label = ' no proxy ';
} elseif (($enableProxyBust == false) && (isset($_SERVER['REMOTE_ADDR']))) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $ipProxy = '';
    $ipProxy_label = '';
} else {
    $ip = '';
    $ipProxy = '';
    $ipProxy_label = '';
}

为了使这些动态可用在下面的函数或查询/回显/视图中,例如用于日志生成或错误报告,请在不需要的情况下使用全局变量或仅在需要的地方回显它们,而不会产生大量其他条件或静态模式输出功能。

function fooNow() {
    global $ip, $ipProxy, $ipProxy_label;
    // begin this actions such as log, error, query, or report
}

感谢您的所有宝贵意见。请让我知道这是否可以更好,但是对于这些标头还是有点新:)


1

我想出了这个函数,它不只是返回IP地址,而是一个包含IP信息的数组。

// Example usage:
$info = ip_info();
if ( $info->proxy ) {
    echo 'Your IP is ' . $info->ip;
} else {
    echo 'Your IP is ' . $info->ip . ' and your proxy is ' . $info->proxy_ip;
}

功能如下:

/**
 * Retrieves the best guess of the client's actual IP address.
 * Takes into account numerous HTTP proxy headers due to variations
 * in how different ISPs handle IP addresses in headers between hops.
 *
 * @since 1.1.3
 *
 * @return object {
 *         IP Address details
 *
 *         string $ip The users IP address (might be spoofed, if $proxy is true)
 *         bool $proxy True, if a proxy was detected
 *         string $proxy_id The proxy-server IP address
 * }
 */
function ip_info() {
    $result = (object) array(
        'ip' => $_SERVER['REMOTE_ADDR'],
        'proxy' => false,
        'proxy_ip' => '',
    );

    /*
     * This code tries to bypass a proxy and get the actual IP address of
     * the visitor behind the proxy.
     * Warning: These values might be spoofed!
     */
    $ip_fields = array(
        'HTTP_CLIENT_IP',
        'HTTP_X_FORWARDED_FOR',
        'HTTP_X_FORWARDED',
        'HTTP_X_CLUSTER_CLIENT_IP',
        'HTTP_FORWARDED_FOR',
        'HTTP_FORWARDED',
        'REMOTE_ADDR',
    );
    foreach ( $ip_fields as $key ) {
        if ( array_key_exists( $key, $_SERVER ) === true ) {
            foreach ( explode( ',', $_SERVER[$key] ) as $ip ) {
                $ip = trim( $ip );

                if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) !== false ) {
                    $forwarded = $ip;
                    break 2;
                }
            }
        }
    }

    // If we found a different IP address then REMOTE_ADDR then it's a proxy!
    if ( $forwarded != $result->ip ) {
        $result->proxy = true;
        $result->proxy_ip = $result->ip;
        $result->ip = $forwarded;
    }

    return $result;
}

1

就像以前有人说过的,这里的密钥是出于什么原因要存储用户的ip。

我将举一个我正在使用的注册系统为例,当然,该解决方案只是为了在我的搜索中经常出现的这个古老的讨论中做出贡献。

许多php注册库都使用ip来根据用户的ip限制/锁定失败的尝试。考虑此表:

-- mysql
DROP TABLE IF EXISTS `attempts`;
CREATE TABLE `attempts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `ip` varchar(39) NOT NULL, /*<<=====*/
  `expiredate` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 -- sqlite
...

然后,当用户尝试登录或进行任何与服务相关的操作(例如密码重置)时,将在开始时调用一个函数:

public function isBlocked() {
      /*
       * used one of the above methods to capture user's ip!!!
       */
      $ip = $this->ip;
      // delete attempts from this ip with 'expiredate' in the past
      $this->deleteAttempts($ip, false);
      $query = $this->dbh->prepare("SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ?");
      $query->execute(array($ip));
      $attempts = $query->fetchColumn();
      if ($attempts < intval($this->token->get('attempts_before_verify'))) {
         return "allow";
      }
      if ($attempts < intval($this->token->get('attempts_before_ban'))) {
         return "captcha";
      }
      return "block";
   }

举例来说,假设有$this->token->get('attempts_before_ban') === 102个用户使用与以前的代码相同的ip 来伪造标头,而在之前的代码中,可以欺骗标头,然后在5次尝试之后,每个用户都被禁止!甚至更糟的是,如果所有用户都来自同一代理,则只会记录前10个用户,其余所有用户将被禁止!

这里的关键是我们需要表上的唯一索引,attempts并且可以通过类似的组合来获取它:

 `ip` varchar(39) NOT NULL,
 `jwt_load varchar(100) NOT NULL

其中jwt_load来自遵循json网络令牌技术的http cookie,其中我们仅存储加密的有效负载,该有效负载包含每个用户的任意/唯一值。当然,该请求应修改为:"SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ? AND jwt_load = ?"并且该类还应启动一个private $jwt


0

我确实想知道是否应该以相反的顺序遍历爆炸的HTTP_X_FORWARDED_FOR,因为我的经验是用户的IP地址最终以逗号分隔的列表结尾,所以从标题的开头开始,更有可能获得返回的代理之一的ip地址,由于许多用户可能通过该代理来访问,因此这可能仍允许会话劫持。


1
阅读了HTTP_X_FORWARDED_FOR上的维基百科页面: en.wikipedia.org/wiki/X-Forwarded-For ...我发现建议的顺序确实是从左到右,因为您的代码具有该顺序。但是,从我们的日志中,我可以看到,在很多情况下,野外代理均不遵守此规定,您要检查的IP地址可能在列表的两端。
克里斯·威瑟斯

1
或在中间,如果某些代理人遵循从左到右的顺序而其他代理人则不遵守这种顺序,就会发生这种情况。
Brilliand

0

谢谢你,非常有用。

如果代码在语法上正确,那将有所帮助。因为它是第20行附近的{,所以恐怕没有人真正尝试过这个。

我可能疯了,但是在尝试了几个有效和无效地址之后,唯一起作用的validate_ip()版本是:

    public function validate_ip($ip)
    {
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE) === false)
            return false;
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) === false)
            return false;
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false)
            return false;

        return true;
    }

0

如果您使用CloudFlare缓存层服务,则为修改后的版本

function getIP()
{
    $fields = array('HTTP_X_FORWARDED_FOR',
                    'REMOTE_ADDR',
                    'HTTP_CF_CONNECTING_IP',
                    'HTTP_X_CLUSTER_CLIENT_IP');

    foreach($fields as $f)
    {
        $tries = $_SERVER[$f];
        if (empty($tries))
            continue;
        $tries = explode(',',$tries);
        foreach($tries as $try)
        {
            $r = filter_var($try,
                            FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 |
                            FILTER_FLAG_NO_PRIV_RANGE |
                            FILTER_FLAG_NO_RES_RANGE);

            if ($r !== false)
            {
                return $try;
            }
        }
    }
    return false;
}

0

只是答案的VB.NET版本:

Private Function GetRequestIpAddress() As IPAddress
    Dim serverVariables = HttpContext.Current.Request.ServerVariables
    Dim headersKeysToCheck = {"HTTP_CLIENT_IP", _
                              "HTTP_X_FORWARDED_FOR", _
                              "HTTP_X_FORWARDED", _
                              "HTTP_X_CLUSTER_CLIENT_IP", _
                              "HTTP_FORWARDED_FOR", _
                              "HTTP_FORWARDED", _
                              "REMOTE_ADDR"}
    For Each thisHeaderKey In headersKeysToCheck
        Dim thisValue = serverVariables.Item(thisHeaderKey)
        If thisValue IsNot Nothing Then
            Dim validAddress As IPAddress = Nothing
            If IPAddress.TryParse(thisValue, validAddress) Then
                Return validAddress
            End If
        End If
    Next
    Return Nothing
End Function

3
问题中有标记“ PHP”
luchaninov

0

只是另一种干净的方式:

  function validateIp($var_ip){
    $ip = trim($var_ip);

    return (!empty($ip) &&
            $ip != '::1' &&
            $ip != '127.0.0.1' &&
            filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false)
            ? $ip : false;
  }

  function getClientIp() {
    $ip = @$this->validateIp($_SERVER['HTTP_CLIENT_IP']) ?:
          @$this->validateIp($_SERVER['HTTP_X_FORWARDED_FOR']) ?:
          @$this->validateIp($_SERVER['HTTP_X_FORWARDED']) ?:
          @$this->validateIp($_SERVER['HTTP_FORWARDED_FOR']) ?:
          @$this->validateIp($_SERVER['HTTP_FORWARDED']) ?:
          @$this->validateIp($_SERVER['REMOTE_ADDR']) ?:
          'LOCAL OR UNKNOWN ACCESS';

    return $ip;
  }

0

从Symfony的Request类 https://github.com/symfony/symfony/blob/1bd125ec4a01220878b3dbc3ec3156b073996af9/src/Symfony/Component/HttpFoundation/Request.php

const HEADER_FORWARDED = 'forwarded';
const HEADER_CLIENT_IP = 'client_ip';
const HEADER_CLIENT_HOST = 'client_host';
const HEADER_CLIENT_PROTO = 'client_proto';
const HEADER_CLIENT_PORT = 'client_port';

/**
 * Names for headers that can be trusted when
 * using trusted proxies.
 *
 * The FORWARDED header is the standard as of rfc7239.
 *
 * The other headers are non-standard, but widely used
 * by popular reverse proxies (like Apache mod_proxy or Amazon EC2).
 */
protected static $trustedHeaders = array(
    self::HEADER_FORWARDED => 'FORWARDED',
    self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
    self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
    self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
    self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
);

/**
 * Returns the client IP addresses.
 *
 * In the returned array the most trusted IP address is first, and the
 * least trusted one last. The "real" client IP address is the last one,
 * but this is also the least trusted one. Trusted proxies are stripped.
 *
 * Use this method carefully; you should use getClientIp() instead.
 *
 * @return array The client IP addresses
 *
 * @see getClientIp()
 */
public function getClientIps()
{
    $clientIps = array();
    $ip = $this->server->get('REMOTE_ADDR');
    if (!$this->isFromTrustedProxy()) {
        return array($ip);
    }
    if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) {
        $forwardedHeader = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]);
        preg_match_all('{(for)=("?\[?)([a-z0-9\.:_\-/]*)}', $forwardedHeader, $matches);
        $clientIps = $matches[3];
    } elseif (self::$trustedHeaders[self::HEADER_CLIENT_IP] && $this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) {
        $clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP])));
    }
    $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
    $firstTrustedIp = null;
    foreach ($clientIps as $key => $clientIp) {
        // Remove port (unfortunately, it does happen)
        if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) {
            $clientIps[$key] = $clientIp = $match[1];
        }
        if (!filter_var($clientIp, FILTER_VALIDATE_IP)) {
            unset($clientIps[$key]);
        }
        if (IpUtils::checkIp($clientIp, self::$trustedProxies)) {
            unset($clientIps[$key]);
            // Fallback to this when the client IP falls into the range of trusted proxies
            if (null ===  $firstTrustedIp) {
                $firstTrustedIp = $clientIp;
            }
        }
    }
    // Now the IP chain contains only untrusted proxies and the client IP
    return $clientIps ? array_reverse($clientIps) : array($firstTrustedIp);
}

未定义的属性:$ server
C47

0

我很惊讶没有人提到filter_input,因此这是Alix Axel的答案,简而言之:

function get_ip_address(&$keys = ['HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'])
{
    return empty($keys) || ($ip = filter_input(INPUT_SERVER, array_pop($keys), FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE))? $ip : get_ip_address($keys);
}

-1

您几乎回答了自己的问题!:)

function getRealIpAddr() {
    if(!empty($_SERVER['HTTP_CLIENT_IP']))   //Check IP address from shared Internet
    {
        $IPaddress = $_SERVER['HTTP_CLIENT_IP'];
    }
    elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))   //To check IP address is passed from the proxy
    {
        $IPaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
    }
    else
    {
        $IPaddress = $_SERVER['REMOTE_ADDR'];
    }
    return $IPaddress;
}

资源


-6
/**
 * Sanitizes IPv4 address according to Ilia Alshanetsky's book
 * "php|architect?s Guide to PHP Security", chapter 2, page 67.
 *
 * @param string $ip An IPv4 address
 */
public static function sanitizeIpAddress($ip = '')
{
if ($ip == '')
    {
    $rtnStr = '0.0.0.0';
    }
else
    {
    $rtnStr = long2ip(ip2long($ip));
    }

return $rtnStr;
}

//---------------------------------------------------

/**
 * Returns the sanitized HTTP_X_FORWARDED_FOR server variable.
 *
 */
public static function getXForwardedFor()
{
if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
    {
    $rtnStr = $_SERVER['HTTP_X_FORWARDED_FOR'];
    }
elseif (isset($HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR']))
    {
    $rtnStr = $HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR'];
    }
elseif (getenv('HTTP_X_FORWARDED_FOR'))
    {
    $rtnStr = getenv('HTTP_X_FORWARDED_FOR');
    }
else
    {
    $rtnStr = '';
    }

// Sanitize IPv4 address (Ilia Alshanetsky):
if ($rtnStr != '')
    {
    $rtnStr = explode(', ', $rtnStr);
    $rtnStr = self::sanitizeIpAddress($rtnStr[0]);
    }

return $rtnStr;
}

//---------------------------------------------------

/**
 * Returns the sanitized REMOTE_ADDR server variable.
 *
 */
public static function getRemoteAddr()
{
if (isset($_SERVER['REMOTE_ADDR']))
    {
    $rtnStr = $_SERVER['REMOTE_ADDR'];
    }
elseif (isset($HTTP_SERVER_VARS['REMOTE_ADDR']))
    {
    $rtnStr = $HTTP_SERVER_VARS['REMOTE_ADDR'];
    }
elseif (getenv('REMOTE_ADDR'))
    {
    $rtnStr = getenv('REMOTE_ADDR');
    }
else
    {
    $rtnStr = '';
    }

// Sanitize IPv4 address (Ilia Alshanetsky):
if ($rtnStr != '')
    {
    $rtnStr = explode(', ', $rtnStr);
    $rtnStr = self::sanitizeIpAddress($rtnStr[0]);
    }

return $rtnStr;
}

//---------------------------------------------------

/**
 * Returns the sanitized remote user and proxy IP addresses.
 *
 */
public static function getIpAndProxy()
{
$xForwarded = self::getXForwardedFor();
$remoteAddr = self::getRemoteAddr();

if ($xForwarded != '')
    {
    $ip    = $xForwarded;
    $proxy = $remoteAddr;
    }
else
    {
    $ip    = $remoteAddr;
    $proxy = '';
    }

return array($ip, $proxy);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.