2011-09-30 15 views
8

誰もが、この入力かどうかを確認するために効果的かつ安全な方法を知っている:一貫性のないフィルタのこの配列に似た何かに対するPHPでIPアドレスとワイルドカードを比較する最適化された方法はありますか?

$_SERVER['REMOTE_ADDR'] 

が一致(200.100は* *だけ200.100のように表すことができること*注意してください。。。)

array(
    '192.168.1.*', 
    '192.168.2.1*', 
    '10.0.0.*', 
    '200.100.*.*', 
    '300.200.*', 
) 

更新

思考:* 'sが示されたワイルドカードと?

foreach($instanceSettings['accessControl']['allowedIpV4Addresses'] as $ipV4Address) { 
    echo 'Now checking against '.$ipV4Address.'.'; 

    // Compare each octet 
    $ipV4AddressOctets = String::explode('.', $ipV4Address); 
    $remoteIpV4AddressOctets = String::explode('.', $_SERVER['REMOTE_ADDR']); 
    $remoteIpV4AddressIsAllowed = true; 
    for($i = 0; $i < Arr::size($ipV4AddressOctets); $i++) { 
     echo 'Comparing '.$ipV4AddressOctets[$i].' against '.$remoteIpV4AddressOctets[$i].'.'; 
     if($ipV4AddressOctets[$i] != $remoteIpV4AddressOctets[$i] && $ipV4AddressOctets[$i] != '*') { 
      echo 'No match.'; 
      $remoteIpV4AddressIsAllowed = false; 
      break; 
     } 
    } 

    // Get out of the foreach if we've found a match 
    if($remoteIpV4AddressIsAllowed) { 
     break; 
    } 
} 
+0

各IPアドレスのネットマスクも指定できますか? 「192.168.100.251/26」または「192.168.100」となります。251 '=>' 26 ''(26ビットマスクは与えられたIPアドレスでは有効でないかもしれませんが、それは単なる例のためです) ネットマスクを指定できれば、IPアドレスの妥当性を計算することは ' $ first_addr_of_mask> $ ip && $ last_addr_of_mask <$ ip) ' – Oerd

+0

私はPHPではなくサーバのファイアウォールでこれを行います。 – hornetbzz

答えて

1

なぜ正規表現を使用しないのですか?

preg_match("((192\\.168\\.1)|(10\\.0\\.0)|(127\\.0\\.0)\\.[012]\\d{0,2}|(\\:\\:1))",$_SERVER['REMOTE_ADDR']) 
+0

私は、配列の内容に基づいて正規表現を動的に生成できると思います。フィルタ配列が動的であることをより明確にするために質問を更新しました。 –

+0

この解決方法は質問に必要な "最適化"する必要はありません。一般的に、IPは整数(unsignedまたはlong)として扱われ、整数を整数の配列と比較する方が高速です。 – Oerd

5

アスタリスクを削除し、ちょうど行います

$ips = array('192.168.1.', '10.0.0.'); 

foreach ($ips as $ip) { 
    if (strpos($_SERVER['REMOTE_ADDR'], $ip) === 0) { 
     // match 
    } 
} 
+0

は文字列に対して正規表現の配列を一致させる方が速いです。 )。 – Anush

+0

'192.168。*。*'については?また、$ ip文字列から '..'を削除する必要があります。 – Oerd

+0

そのようなものは捕まえません:255。*。255.255。 – MasterCassim

0

楽しみのためだけに、私はオーバーエンジニアこれにつもりです。まあ、あなたと比べるとかなり長いリストがなければ。

「このオクテットは気にしません」という意味でワイルドカードを使用していると仮定すると、アレイの各エントリを4つの値(1オクテットごとに1つ)に解析できます。ワイルドカードを意味するために-1を使用すると、0-255はその値と正確に一致することを意味します。 (O(n)より良いパフォーマンスが必要な場合は、nがマッチリストのサイズです。ここでは、より良いデータ構造、たとえばトライなどがあります)。この配列Lを呼び出します。もちろん、あなたはこれを1度だけ行う必要があり、要求ごとに行う必要はありません。

次に、ワイルドカードを除いて同じ方法でリモートアドレスを解析できます。あなたがここにもそれが今の一致を確認するために、かなり些細なりREMOTE_ADDRが期待される形式にされていないキャッチすることができます。

has_match(ip) = 
    for n in [0 … L.length) 
    if (-1 == L.n.0 || L.n.0 = ip.0) && (-1 == L.n.1 || L.n.1 == ip.1) && … 
     return true 
    return false 

(それはもちろん、擬似コードです)

+0

@stereofrog:Refalを使用したことはありません(あなたがそれを言及する前に聞いたことはありません)。その可能性のある人から擬似コードを読んだことはかなり可能です。私はそれが擬似コードにどのように似ているかについてはあまりよく分かっていませんが、添え字には '.'以外のものがあります。 – derobert

8

私はこれをベンチは、マークされていません、私は0と任意の*を置き換えネットワーキングハードウェア/ソフトウェアを使用する方法...

を使用することを選ぶだろうと255 は整数

ので255.255.255場合にIPアドレスを変換します。*は255.255.255.0になり255.255.255.255 次に、これら2つのipsでip2long関数を実行します。

次に、指定したipをlong ipに変換できます。たとえば、255.255.50.51を長いIPに変換します。

次に、この指定されたIPのロングIPがブラックリストの変換されたロングipsの間であるかどうかを比較できます。そうであれば、他には許されない。

$ips = array("ip1", "ip2"); 
foreach($ips as $ip){ 
$ip1 = str_replace("*", "0", $ip); 
$ip2 = str_replace("*", "255", $ip); 

$ip1 = ip2long($ip1); 
$ip2 = ip2long($ip2); 
$givenip = $_GET["ip"]; 
$givenip = ip2long($givenip); 

if($givenip >= $ip1 && $ip <= $givenip){ 
    echo "blacklist ip hit between {$ip1} and {$ip2} on {$ip}"; 
} 
} 
+1

文字列比較の代わりにip2longと数値比較の場合は –

+2

リスト内の2番目のIPが解決策を破ります: '192.168.2.1 *'は '192.168.2.10'となり、' 192.168.2.1255'になります。最初のものは私たちが望むものではありませんが、2番目のIPアドレスは全く違法です。 – Oerd

+1

xxx0の範囲を検出するために '$ ip <= $ givenip'を' $ givenip <= $ ip2'にしてはいけません - xxx255? – Lekensteyn

2

アスタリスクが123.123でない質問とショートマスクのすべてのケースを許可します。

/** 
* Checks given IP against array of masks like 123.123.123.123, 123.123.*.101, 123.123., 123.123.1*.* 
* 
* @param $ip 
* @param $masks 
* @return bool 
*/ 
public static function checkIp($ip, $masks) 
{ 
    if (in_array($ip, $masks)) { 
     return true; // Simple match 
    } else { 
     foreach ($masks as $mask) { 
      if (substr($mask, -1) == '.' AND substr($ip, 0, strlen($mask)) == $mask) { 
       return true; // Case for 123.123. mask 
      } 
      if (strpos($mask, '*') === false) { 
       continue; // No simple matching and no wildcard in the mask, leaves no chance to match 
      } 
      // Breaking into triads 
      $maskParts = explode('.', $mask); 
      $ipParts = explode('.', $ip); 
      foreach ($maskParts as $key => $maskPart) { 
       if ($maskPart == '*') { 
        continue; // This triad is matching, continue with next triad 
       } elseif (strpos($maskPart, '*') !== false) { 
        // Case like 1*, 1*2, *1 
        // Let's use regexp for this 
        $regExp = str_replace('*', '\d{0,3}', $maskPart); 
        if (preg_match('/^' . $regExp . '$/', $ipParts[$key])) { 
         continue; // Matching, go to check next triad 
        } else { 
         continue 2; // Not matching, Go to check next mask 
        } 
       } else { 
        if ($maskPart != $ipParts[$key]) { 
         continue 2; // If triad has no wildcard and not matching, check next mask 
        } 
        // otherwise just continue 
       } 
      } 
      // We checked all triads and all matched, hence this mask is matching 
      return true; 
     } 
     // We went through all masks and none has matched. 
     return false; 
    } 
} 
関連する問題