检查IP地址是否为私有

时间:2012-12-11 10:16:32

标签: php ip private

我想检查一个IP地址是否在私有网络中。它不起作用。

我的代码:

<?php
$ip = $_SERVER['REMOTE_ADDR'];

function _isPrivate($ip) 
{
    $i = explode('.', $ip);

    if ($i[0] == 10) {
        return true;
    } else if ($i[0] == 172 && $i[1] > 15 && $i[1] < 32) {
        return true;
    } else if ($i[0] == 192 && $i[1] == 168) {
        return true;
    }
    return false;
}
?>

另一个:

<?php
$ip = $_SERVER['REMOTE_ADDR'];

function _isPrivate($ip) 
{
    $ip = ip2long($ip);
    $net_a = ip2long('10.255.255.255') >> 24; 
    $net_b = ip2long('172.31.255.255') >> 20; 
    $net_c = ip2long('192.168.255.255') >> 16; 

    return $ip >> 24 === $net_a || $ip >> 20 === $net_b || $ip >> 16 === $net_c; 
}
?>

非常感谢任何帮助,谢谢!

6 个答案:

答案 0 :(得分:67)

我认为这应该可以解决问题。

如果IP地址是私有地址,则与filter_var一起使用的

validation rules将返回false。

$user_ip = '127.0.0.1';
filter_var(
    $user_ip, 
    FILTER_VALIDATE_IP, 
    FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE |  FILTER_FLAG_NO_RES_RANGE
)

检查上面的链接以获取php文档

答案 1 :(得分:22)

function ip_is_private ($ip) {
    $pri_addrs = array (
                      '10.0.0.0|10.255.255.255', // single class A network
                      '172.16.0.0|172.31.255.255', // 16 contiguous class B network
                      '192.168.0.0|192.168.255.255', // 256 contiguous class C network
                      '169.254.0.0|169.254.255.255', // Link-local address also refered to as Automatic Private IP Addressing
                      '127.0.0.0|127.255.255.255' // localhost
                     );

    $long_ip = ip2long ($ip);
    if ($long_ip != -1) {

        foreach ($pri_addrs AS $pri_addr) {
            list ($start, $end) = explode('|', $pri_addr);

             // IF IS PRIVATE
             if ($long_ip >= ip2long ($start) && $long_ip <= ip2long ($end)) {
                 return true;
             }
        }
    }

    return false;
}

请参阅http://mebsd.com/coding-snipits/check-private-ip-function-php.html

您可能还想查看有关私有地址空间here

的信息

答案 2 :(得分:3)

...我的5美分:

恕我直言,基本问题只是“如何检查IP地址是否属于网络?”。

答案很简单:IP_address AND network_mask EQUALS network_address。

例如,IP地址10.1.2.3是否属于网络10.0.0.0,网络掩码为255.0.0.0? 10.1.2.3&amp; 255.0.0.0是10.0.0.0,所以答案是:是的。

更容易在二进制文件中看到它:

  00001010 00000001 00000010 00000011 ( 10.1.2.3) ip address
& 11111111 00000000 00000000 00000000 (255.0.0.0) network mask
= 00001010 00000000 00000000 00000000 ( 10.0.0.0) network address

只需要检查你需要的网络(包括或不包括环回,链接本地等):

function _isPrivate($long_ip) {
    return ( ($long_ip & 0xFF000000) === 0x0A000000 ) || //Private A network: 00001010 ....
           ( ($long_ip & 0xFFF00000) === 0xAC100000 ) || //Private B network: 10101100 0001....
           ( ($long_ip & 0xFFFF0000) === 0xC0A80000 ) || //Private C network: 11000000 10101000 ....
           //Link-local and loopback are NOT private range, so the function in the question yield right results to "is in private range?". Seems it was not the desired behaviour... Those cases can also be checked:
           ( ($long_ip & 0xFFFF0000) === 0xA9FE0000 ) || //Link-local       : 10101001 11111110 ....
           ( ($long_ip & 0xFFFF0000) === 0x7F000000 ) || //Loopback         : 01111111 ....
         //...and add all the fancy networks that you want...
           ( ($long_ip & 0xFFFFFF00) === 0xC0AF3000 ) || //Direct Delegation AS112 Service 192.175.48.0/24...
           ( ($long_ip & 0xF0000000) === 0xF0000000 ); //Reserved 240.0.0.0/4
}

有趣的一点是否定了返回值。返回的值实际上并不意味着给定的IP位于专用网络中,但它的否定确实意味着给定的IP是“公共IP地址”(公共/普通IP地址),因为用户4880112的解决方案明确了这一点。

<强>的IPv6

IPv6也是如此。 “私人网络”地址(正式称为“Unique-Local”,RFC 4193)是“fc00 :: / 7”。所以,ip_address&amp; 0xFE00 .. === 0xFC00 ..是“私人网络”

采纳上述答案并包含来自IANA的最新信息......

http://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml http://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml

...我们可以像这样做一些更通用的功能:

function isPublicAddress($ip) {
  // returns false on failure.
  // negative if it's a private or special address (-4:IPv4, -16:IPv6)
  // positive if it's a common IP public address (4:IPv4, 16:IPv6)

  $networks = array(
    '4' => array('0.0.0.0/8',
      '10.0.0.0/8',
      '100.64.0.0/10',
      '127.0.0.0/8',
      '169.254.0.0/16',
      '172.16.0.0/12',
      '192.0.0.0/24',
      '192.0.0.0/29',
      '192.0.0.8/32',
      '192.0.0.9/32',
      '192.0.0.170/32',
      '192.0.0.170/32',
      '192.0.2.0/24',
      '192.31.196.0/24',
      '192.52.193.0/24',
      '192.88.99.0/24',
      '192.168.0.0/16',
      '192.175.48.0/24',
      '198.18.0.0/15',
      '198.51.100.0/24',
      '203.0.113.0/24',
      '240.0.0.0/4',
      '255.255.255.255/32')
    ,
    '16' => array('::1/128',
      '::/128',
      '::ffff:0:0/96',
      '64:ff9b::/96',
      '100::/64',
      '2001::/23',
      '2001::/32',
      '2001:1::1/128',
      '2001:2::/48',
      '2001:3::/32',
      '2001:4:112::/48',
      '2001:5::/32',
      '2001:10::/28',
      '2001:20::/28',
      '2001:db8::/32',
      '2002::/16',
      '2620:4f:8000::/48',
      'fc00::/7',
      'fe80::/10') 
    );

    $ip = inet_pton($ip);
    if( $ip === false ) return false;

    $space='16';
    if (strlen($ip) === 4) { 
      $space='4';
    }

    //Is the IP in a private or special range?
    foreach($networks[$space] as $network) {
      //split $network in address and mask
      $parts=explode('/',$network);
      $network_address = inet_pton($parts[0]);
      $network_mask    = inet_pton( _mask( $ip , $parts[1] ) );
      if (($ip & $network_mask) === $network_address){
        return -1*$space;
      }
    }
    //Success!
    return $space;
}

function _mask($ip,$nbits){
  $mask='';
  $nibble=array('0','8','C','E');
  $f_s= $nbits >> 2 ;
  if( $f_s > 0 ) $mask.=str_repeat('F',$f_s);
  if( $nbits % 4 ) $mask.= $nibble[$nbits % 4];
  if( strlen($ip) === 4 ){
    if( strlen($mask) < 8 ) $mask.=str_repeat('0', 8 - strlen($mask) );
    long2ip('0x'.$mask);
    $mask=long2ip('0x'.$mask);
  }else{
    if( strlen($mask) < 32 ) $mask.=str_repeat('0', 32 - strlen($mask) );
    $mask=rtrim(chunk_split($mask,4,':'),':');
  }
  return $mask;
}

我现在想知道的是:“IPv4映射地址”中的IPv6地址是IPv6中的“特殊”地址,即使它是IPv4中的“正常”IP地址。我们是否应该考虑“私有使用”:: ffff:0:0/96中与IPv4私有网络匹配的子网?

编辑解释最后一次讽刺:

IPv6网络:: ffff:0:0/96映射到每个IPv4地址的IPv6地址。这些IPv6地址在IANA注册表(“专用”)中是单一集合,但映射的IPv4地址在IPv4中的所有类型集合中(私有网络,环回,广播,公共......)“通用IPv4地址“始终是”特殊的IPv6地址“。如果我们在:: ffff:0:0/96范围内使用与IPv4专用网络匹配的IPv6地址建立网络...我们是否使用专用网络地址?

答案 3 :(得分:2)

好,这是7岁的帖子。但是我认为我可以分享自己的解决方案,这样也许某个地方的某人可能会觉得有用。

我的解决方案基于内置的PHP filter_var()函数。这意味着我不必每次都需要验证给定值时就预定义所有私有范围或保留范围。或遍历范围。相反,我让PHP为我担心。

class IP
{
    static public function is_ip($ip=NULL) : bool
    {
        return filter_var(
            $ip,
            FILTER_VALIDATE_IP,
            FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6
        ) === $ip ? TRUE : FALSE;
    }

    static public function is_ipv4($ip=NULL) : bool
    {
        return filter_var(
            $ip,
            FILTER_VALIDATE_IP,
            FILTER_FLAG_IPV4
        ) === $ip ? TRUE : FALSE;
    }

    static public function is_ipv6($ip=NULL) : bool
    {
        return filter_var(
            $ip,
            FILTER_VALIDATE_IP,
            FILTER_FLAG_IPV6
        ) === $ip ? TRUE : FALSE;
    }

    static public function is_public_ip($ip=NULL) : bool
    {
        return filter_var(
            $ip,
            FILTER_VALIDATE_IP,
            FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
        ) === $ip ? TRUE : FALSE;
    }

    static public function is_public_ipv4($ip=NULL) : bool
    {
        return filter_var(
            $ip,
            FILTER_VALIDATE_IP,
            FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
        ) === $ip ? TRUE : FALSE;
    }

    static public function is_public_ipv6($ip=NULL) : bool
    {
        return filter_var(
            $ip,
            FILTER_VALIDATE_IP,
            FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
        ) === $ip ? TRUE : FALSE;
    }

    static public function is_private_ip($ip=NULL) : bool
    {
        return self::is_ip($ip) && !self::is_public_ip($ip);
    }

    static public function is_private_ipv4($ip=NULL) : bool
    {
        return self::is_ipv4($ip) && !self::is_public_ipv4($ip);
    }

    static public function is_private_ipv6($ip=NULL) : bool
    {
        return self::is_ipv6($ip) && !self::is_public_ipv6($ip);
    }
}

它通常使您可以验证给定的值是否是IP。或更具体些您可以验证以下类型:

  • IP
  • IPv4
  • IPv6
  • 公共IP
  • 公共IPv4
  • 公共IPv6
  • 私有IP
  • 私有IPv4
  • 私有IPv6

让我们对其进行测试:

$arr = array(
    '127.0.0.0', '127.0.0.1', '127.1.2.3', '127.1.2.255',

    '192.168.0.0', '192.168.0.1', '192.168.2.3', '192.168.2.255',

    '172.16.0.0', '172.16.0.1', '172.16.2.3', '172.16.2.255',
    '172.19.0.0', '172.19.0.1', '172.19.2.3', '172.19.2.255',

    '10.0.0.0', '10.0.0.1', '10.0.2.3', '10.0.2.255',
    '10.5.0.0', '10.5.0.1', '10.5.2.3', '10.5.2.255',

    '8.8.8.8', '8.8.4.4', '255.255.255',

    '182.168.1.300', '256.1.2.3', '0.500.0.0',
    'I am not an IP', NULL, '185.128.72.151'
);

foreach ($arr as $item) {
    echo "$item --> " . (IP::is_private_ip($item) === TRUE ? 'is private' : 'is NOT private or NOT an IP') . PHP_EOL;
}

答案 4 :(得分:1)

使用inet_pton而不是ip2long,并包含一些比较模糊的私有范围:

function isPublicAddress($ip) {

    //Private ranges...
    //http://www.iana.org/assignments/iana-ipv4-special-registry/
    $networks = array('10.0.0.0'        =>  '255.0.0.0',        //LAN.
                      '172.16.0.0'      =>  '255.240.0.0',      //LAN.
                      '192.168.0.0'     =>  '255.255.0.0',      //LAN.
                      '127.0.0.0'       =>  '255.0.0.0',        //Loopback.
                      '169.254.0.0'     =>  '255.255.0.0',      //Link-local.
                      '100.64.0.0'      =>  '255.192.0.0',      //Carrier.
                      '192.0.2.0'       =>  '255.255.255.0',    //Testing.
                      '198.18.0.0'      =>  '255.254.0.0',      //Testing.
                      '198.51.100.0'    =>  '255.255.255.0',    //Testing.
                      '203.0.113.0'     =>  '255.255.255.0',    //Testing.
                      '192.0.0.0'       =>  '255.255.255.0',    //Reserved.
                      '224.0.0.0'       =>  '224.0.0.0',        //Reserved.
                      '0.0.0.0'         =>  '255.0.0.0');       //Reserved.

    //inet_pton.
    $ip = @inet_pton($ip);
    if (strlen($ip) !== 4) { return false; }

    //Is the IP in a private range?
    foreach($networks as $network_address => $network_mask) {
         $network_address   = inet_pton($network_address);
         $network_mask      = inet_pton($network_mask);
         assert(strlen($network_address)    === 4);
         assert(strlen($network_mask)       === 4);
         if (($ip & $network_mask) === $network_address)
            return false;
    }

    //Success!
    return true;

}

答案 5 :(得分:0)

基本上是@Mark Davidson的答案,但有点数学。

function isPrivate($szAddr) {

   $nIP = ip2long($szAddr);

   $arLocal = [
      [ip2long('127.0.0.0'),   24],
      [ip2long('10.0.0.0'),    24],
      [ip2long('172.16.0.0'),  20],
      [ip2long('192.168.0.0'), 16],
      [ip2long('169.254.0.0'), 16],
   ];

   foreach( $arLocal as $arP ) {

      $maskLo = ~((1 << $arP[1]) - 1);  // CREATE BIT MASK FROM NUMBER

      if( ($nIP & $maskLo) === $arP[0] ) // BITWISE-AND, THEN COMPARE
         return true;
   }

   return false;
}