2011-09-30 15 views
8

Qualcuno sa di un metodo efficace e sicuro per vedere se questo ingresso:Un metodo ottimizzato per confrontare gli indirizzi IP con i caratteri jolly in PHP?

$_SERVER['REMOTE_ADDR'] 

partite contro qualcosa di simile a questo array di filtri incoerenti (si noti che 200,100 * * potrebbe essere espressa come solo 200,100 *...) con caratteri jolly indicati da s '*:

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

aggiornamento

Pensieri?

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

è possibile specificare anche la maschera di rete di ciascun indirizzo IP? cioè: ''192.168.100.251/26'' o'' 192.168.100.251 '=>' 26'' (quella maschera a 26 bit potrebbe non essere nemmeno valida per l'indirizzo IP dato, è solo per un esempio) Se è possibile specificare maschere di rete, quindi il calcolo della validità dell'indirizzo IP è facile come 'if ($ first_addr_of_mask> $ ip && $ last_addr_of_mask <$ ip) ' – Oerd

+0

Non lo farei in PHP ma piuttosto sul firewall del server. – hornetbzz

risposta

1

Perché non usare solo un'espressione regolare?

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

Suppongo di poter generare dinamicamente un'espressione regolare basata sul contenuto dell'array. Ho aggiornato la domanda per rendere più ovvio che l'array del filtro sarà dinamico. –

+0

questa soluzione non è necessaria "ottimizzata" come la domanda richiede. Generalmente, gli IP vengono trattati meglio come numeri interi (senza segno o lunghi) e il confronto di un intero con un array di interi è più veloce rispetto a un array di espressioni regolari su una stringa (cioè ip) – Oerd

5

Rimuovere gli asterischi e solo fare:

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

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

ha appena scritto ciò che volevo scrivere (bella soluzione!). – Anush

+0

cosa dire di '192.168. *. *'? Dovresti anche rimuovere '..' dalla stringa $ ip. – Oerd

+0

Non prenderebbe qualcosa del genere: 255. *. 255.255. – MasterCassim

0

Solo per divertimento, ho intenzione di over-ingegnerizzare questo. Bene, a meno che tu non abbia una lista abbastanza lunga da abbinare.

Supponendo che si utilizzino solo caratteri jolly per indicare "Non mi interessa di questo ottetto", è possibile analizzare ciascuna voce dell'array in quattro valori (uno per ottetto). Supponiamo che tu usi -1 come wildcard, 0-255 significhi corrispondere esattamente a quel valore. (Se hai bisogno di prestazioni migliori di O (n), dove n è la dimensione della lista di corrispondenze, allora ci sono strutture dati migliori che puoi usare qui - un trie, per esempio.) Chiama questo array L. Naturalmente, hai solo bisogno di fare questa volta, non per richiesta.

È quindi possibile analizzare l'indirizzo remoto nello stesso modo (tranne senza caratteri jolly). Si può anche prendere REMOTE_ADDR non essere nel formato previsto qui Ora diventa abbastanza banale per controllare gli incontri:

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 

(Questo è pseudo-codice, ovviamente)

+0

@stereofrog: Ho paura di non aver mai usato Refal (ne ho sentito parlare prima che me ne fossi accennato). È possibile che abbia letto pseudo-codice da qualcuno che ha, però. Non sono abbastanza sicuro di come sia simile al mio pseudo-codice, anche se ... diverso da '.' per gli indici. – derobert

8

non ho banco segnato questo, ma vorrei scegliere di utilizzare il metodo di rete hardware/software utilizza ...

Sostituire qualsiasi * con 0 e 255. Convertire gli indirizzi IP per interi

Quindi, se 255.255.255. * diventa 255.255.255.0 e 255.255.255.255 Quindi eseguire la funzione ip2long su questi due ips.

Quindi è possibile convertire l'ip indicato in ip lungo. ad esempio 255.255.50.51 in IP lungo.

Quindi è possibile confrontare se l'ip lungo per questo dato ip è tra i lunghi ips convertiti nella lista nera. Se è quindi non è permesso altro lo è.

$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 per il confronto tra ip2long e numerico invece del confronto tra stringhe –

+2

Il secondo IP nell'elenco interromperà la soluzione: '192.168.2.1 *' diventerà '192.168.2.10' e' 192.168.2.1255'. Mentre il primo non è esattamente quello che volevamo, il secondo è un indirizzo IP completamente illegale :) – Oerd

+1

Non dovrebbe '$ ip <= $ givenip' diventare' $ datoip <= $ ip2' per rilevare l'intervallo xxx0 - xxx255? – Lekensteyn

2

Questo consente tutti i casi nella domanda più maschere corte senza asterischi come 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; 
    } 
} 
Problemi correlati