php - Проверьте, является ли IP-адрес частным



private (4)

Мне нравится проверять, находится ли 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; 
}
?>

Любая помощь будет высоко ценится, спасибо!

https://code.i-harness.com


... мои 5 центов:

ИМХО, основной вопрос - просто «как проверить, принадлежит ли IP-адрес сети?».

Ответ прост: двоичный файл: IP_address И network_mask РАВНЫЕ сетевой_адрес.

Например, относится ли IP-адрес 10.1.2.3 к сети 10.0.0.0 с маской сети 255.0.0.0? 10.1.2.3 и 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

Просто нужно проверить, что для нужных вам сетей (включая или нет loopback, link-local и т. Д.):

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 & 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;
}

Теперь мне интересно: IPv6-адрес в «IPv4-сопоставленном адресе» - это «специальный» адрес в IPv6, даже если это был «нормальный» IP-адрес в IPv4. Должны ли мы рассматривать «частное использование» подсетей в :: ffff: 0: 0/96, которые соответствуют частным сетям IPv4?

РЕДАКТИРОВАТЬ, чтобы объяснить последний комментарий:

Сеть IPv6 :: ffff: 0: 0/96 сопоставляется с адресом IPv6 каждый адрес IPv4. Эти IPv6-адреса находятся в одном наборе в реестре IANA («Специального назначения»), но сопоставленные IPv4-адреса находятся во всех видах наборов в IPv4 (частная сеть, loopback, широковещательная, общедоступная ...) A «общий IPv4 адрес "это всегда" специальный адрес IPv6 ". Если мы настроим сеть с использованием адреса IPv6 в диапазоне :: ffff: 0: 0/96, который соответствует частным сетям IPv4 ... Используем ли мы адрес частной сети?


В основном ответ @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;
}

Я думаю, что это должно решить проблему.

filter_var используемый со следующими правилами проверки , вернет false, если IP-адрес является частным.

$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


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;
}

См. mebsd.com/coding-snipits/check-private-ip-function-php.html

Вы могли бы также хотеть проверить о частных адресных пространствах here





private