28

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

どんな助けでも大歓迎です、ありがとう!

4

6 に答える 6

95

これで問題は解決すると思います。

次の検証ルールで使用されるfilter_varは、IP アドレスがプライベート アドレスである場合に 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ドキュメントについては、上記のリンクを確認してください

于 2012-12-11T10:48:25.953 に答える
32
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を参照してください。

プライベート アドレス空間については、こちらもご覧ください。

于 2012-12-11T10:19:42.003 に答える
3

...私の 5 セント:

私見の根底にある質問は、「IPアドレスがネットワークに属しているかどうかを確認する方法」です。

答えは単純なバイナリです: IP_address AND network_mask EQUALS network_address.

たとえば、IP アドレス 10.1.2.3 は、ネットマスク 255.0.0.0 のネットワーク 10.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

必要なネットワークについて確認する必要があります(ループバック、リンクローカルなどを含むかどうか):

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 アドレス) であることを意味します。

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

私が今疑問に思っているのは、「IPv4 マップ アドレス」の IPv6 アドレスは、IPv4 の「通常の」IP アドレスであったとしても、IPv6 の「特別な」アドレスであるということです。::ffff:0:0/96 内の IPv4 私用ネットワークに一致するサブネットを「私的使用」と見なす必要がありますか?

最後のコメントを説明する編集:

IPv6 ネットワーク ::ffff:0:0/96 は、IPv4 アドレスごとに IPv6 アドレスにマップされます。これらの IPv6 アドレスは、IANA レジストリ (「特別目的」) の単一のセットにありますが、マップされた IPv4 アドレスは、IPv4 のあらゆる種類のセット (プライベート ネットワーク、ループバック、ブロードキャスト、パブリック...) にあります。アドレス」は常に「特別な IPv6 アドレス」です。IPv4 プライベート ネットワークに一致する ::ffff:0:0/96 の範囲の IPv6 アドレスを使用してネットワークをセットアップする場合... プライベート ネットワーク アドレスを使用していますか?

于 2016-04-16T16:48:21.787 に答える
1

ip2long の代わりに inet_pton を使用し、よりあいまいなプライベート範囲をいくつか含めます。

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;

}
于 2015-05-09T17:43:32.237 に答える