2011-09-30 14 views
8

Każdy wie, skutecznej i bezpiecznej metody, aby zobaczyć, czy to wejście:Zoptymalizowana metoda porównywania adresów IP ze znakami wieloznacznymi w PHP?

$_SERVER['REMOTE_ADDR'] 

mecze przeciwko coś podobnego do tej tablicy filtrów niespójnych (zauważ, że 200.100 * * może być wyrażona jako tylko 200,100 *...) z symboli wieloznacznych oznaczonych * 's:

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

Aktualizuj

myśli?

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

Czy można również określić maskę sieci dla każdego adresu IP? tj .: "192.168.100.251/26'" lub "192.168.100.251 '=>' 26'' (ta 26-bitowa maska ​​może nie być nawet ważna dla podanego adresu IP, to tylko dla przykładu) Jeśli możesz określić maski sieci, to obliczanie trafności adresu IP jest tak proste jak 'if ($ first_addr_of_mask> $ ip && $ last_addr_of_mask <$ ip) ' – Oerd

+0

Nie zrobiłbym tego w PHP, ale raczej na firewallu serwera. – hornetbzz

Odpowiedz

1

Dlaczego po prostu użyć wyrażenia regularnego?

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

Przypuszczam, że mógłbym dynamicznie generować wyrażenie regularne na podstawie zawartości tablicy. Zaktualizowałem pytanie, aby było bardziej oczywiste, że tablica filtrów będzie dynamiczna. –

+0

to rozwiązanie nie jest konieczne "zoptymalizowane", ponieważ wymaga tego pytanie. Ogólnie rzecz biorąc, adresy IP są traktowane jako liczby całkowite (niepodpisane lub długie), a porównywanie liczby całkowitej z tablicą liczb całkowitych jest szybsze niż dopasowanie tablicy wyrażeń regularnych do ciągu znaków (tj. Ip) – Oerd

5

Wyjąć gwiazdki i po prostu zrobić:

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

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

właśnie napisałem to, co chciałem napisać (dobre rozwiązanie!). – Anush

+0

co z '192.168. *. *'? Trzeba też usunąć '..' z ciągu $ ip. – Oerd

+0

Nie złapałbym czegoś takiego: 255. *. 255.255. – MasterCassim

0

Tylko dla zabawy, mam zamiar to ponad inżynier. Cóż, chyba że masz dość długą listę do porównania.

Zakładając, że używasz tylko symboli wieloznacznych w znaczeniu "Nie obchodzi mnie ten oktet", możesz przeanalizować każdy wpis w tablicy na cztery wartości (po jednym na oktet). Powiedzmy, że używasz -1 do oznaczenia symbolu wieloznacznego, 0-255 oznacza, że ​​dokładnie pasujesz do tej wartości. (Jeśli potrzebujesz lepszej wydajności niż O (n), gdzie n jest wielkością listy dopasowań, to istnieją lepsze struktury danych, których możesz użyć tutaj - na przykład trie.) Zadzwoń do tej tablicy L. Oczywiście, potrzebujesz tylko zrobić to raz - nie na żądanie.

Następnie można przeanalizować zdalny adres w ten sam sposób (z wyjątkiem bez symboli wieloznacznych). Można również skorzystać REMOTE_ADDR nie będąc w oczekiwanym formacie tu teraz staje się to dość trywialne, by sprawdzić mecze:

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 

(To pseudo-kod, oczywiście)

+0

@stereofrog: Obawiam się, że nigdy nie użyłem Refal (ani o nim nie słyszałem, zanim to wspomniałeś). Jest całkiem możliwe, że przeczytałem pseudo-kod od kogoś, kto ma. Nie jestem do końca pewien, jak przypomina mój pseudo-kod, chociaż ... inny niż "." Dla indeksów dolnych. – derobert

8

Nie ława oznakowane to, ale chciałbym zdecydować się na stosowanie metody, która używa sieci sprzętu/oprogramowania ...

Wymień * z 0 i 255. Konwersja adresów IP do liczb całkowitych

Więc jeśli 255.255.255. * staje 255.255.255.0 i 255.255.255.255 Następnie wykonaj funkcję ip2long na tych dwóch ips.

Następnie możesz przekonwertować podane IP na długie IP. na przykład 255.255.50.51 w długie IP.

Następnie można porównać, czy długi adres IP dla tego podanego adresu IP znajduje się pomiędzy przekonwertowanym długim ips na czarnej liście. Jeśli tak jest, to nie jest dozwolone inaczej.

$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

+1 dla porównania ip2long i numerycznego zamiast porównywania ciągów znaków –

+2

Drugi adres IP na liście przerwałby rozwiązanie: '192.168.2.1 *' stanie się "192.168.2.10" i "192.168.2.1255". Podczas gdy pierwsza nie jest dokładnie tym, czego chcieliśmy, druga to całkowicie nielegalny adres IP :) – Oerd

+1

Nie powinno "$ ip <= $ givenip' stać się' $ givenip <= $ ip2' w celu wykrycia zakresu xxx0 - xxx255? – Lekensteyn

2

Ten pozwala na wszystkie przypadki w pytaniu plus krótkie maski bez gwiazdek jak 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; 
    } 
} 
Powiązane problemy