2011-09-30 30 views
8

Bất cứ ai biết về một phương pháp hiệu quả và an toàn để xem nếu đầu vào này:Một phương pháp tối ưu để so sánh địa chỉ IP với các ký tự đại diện trong PHP?

$_SERVER['REMOTE_ADDR'] 

trận đấu chống lại một cái gì đó tương tự như mảng này của bộ lọc không phù hợp (lưu ý rằng 200,100 * * có thể được diễn tả như chỉ 200,100 *...) với các kí hiệu chỉ định bởi * 's:

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

cập nhật

Suy nghĩ?

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

bạn cũng có thể chỉ định mặt nạ mạng của từng địa chỉ IP không? tức là: '192.168.100.251/26'' hoặc '' 192.168.100.251 '=>' 26'' (mặt nạ 26 bit đó có thể không hợp lệ đối với địa chỉ IP đã cho, nó chỉ vì mục đích của ví dụ) Nếu bạn có thể chỉ định mặt nạ mạng, thì việc tính hiệu lực địa chỉ IP cũng dễ dàng như 'if ($ first_addr_of_mask> $ ip && $ last_addr_of_mask <$ ip) ' – Oerd

+0

Tôi sẽ không làm điều này trong PHP mà là trên tường lửa máy chủ. – hornetbzz

Trả lời

1

Tại sao không chỉ sử dụng cụm từ thông dụng?

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

Tôi cho rằng tôi có thể tự động tạo một regex dựa trên nội dung của mảng. Tôi cập nhật các câu hỏi để làm cho nó rõ ràng hơn rằng mảng lọc sẽ được năng động. –

+0

giải pháp này là không cần thiết "tối ưu hóa" như câu hỏi yêu cầu. Nói chung, IP được xử lý tốt hơn như số nguyên (unsigned hoặc long) và so sánh một số nguyên với một mảng số nguyên là nhanh hơn phù hợp với một mảng các biểu thức chính quy đối với một chuỗi (tức là ip) – Oerd

5

Hủy bỏ các dấu sao và chỉ cần làm:

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

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

chỉ viết những gì tôi muốn viết (giải pháp tốt đẹp!). – Anush

+0

những gì về '192.168. *. *'? Bạn cũng cần xóa '..' khỏi chuỗi $ ip. – Oerd

+0

Sẽ không bắt được thứ gì đó như thế: 255. *. 255.255. – MasterCassim

0

Chỉ cần cho vui, tôi sẽ đến hơn-kỹ sư này. Vâng, trừ khi bạn có một danh sách khá dài để phù hợp với.

Giả sử bạn chỉ sử dụng ký tự đại diện có nghĩa là "Tôi không quan tâm đến octet này", sau đó bạn có thể phân tích từng mục nhập trong mảng thành bốn giá trị (một giá trị mỗi octet). Giả sử bạn sử dụng -1 để có nghĩa là ký tự đại diện, 0–255 có nghĩa là khớp chính xác với giá trị đó. (Nếu bạn cần hiệu suất tốt hơn O (n), trong đó n là kích thước của danh sách đối sánh, thì có cấu trúc dữ liệu tốt hơn bạn có thể sử dụng tại đây — ví dụ: trie.) Gọi mảng này L. Tất nhiên, bạn chỉ cần thực hiện việc này một lần — không phải theo yêu cầu.

Sau đó, bạn có thể phân tích cú pháp địa chỉ từ xa theo cùng một cách (ngoại trừ không có ký tự đại diện). Bạn cũng có thể bắt REMOTE_ADDR không phải là theo định dạng mong đợi ở đây bây giờ Nó trở thành khá tầm thường để kiểm tra các trận đấu:

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 

(Đó là pseudo-code, tất nhiên)

+0

@stereofrog: Tôi sợ rằng tôi chưa bao giờ sử dụng Refal (cũng không nghe nói về nó trước khi bạn đề cập đến nó). Nó khá có thể tôi đã đọc giả mã từ một người có, mặc dù. Tôi không hoàn toàn chắc chắn như thế nào nó giống như mã giả của tôi, mặc dù ... khác với '.' cho subscripts. – derobert

8

tôi đã không băng ghế dự bị-đánh dấu này, nhưng tôi sẽ lựa chọn để sử dụng các phương pháp mà mạng phần cứng/phần mềm sử dụng ...

Thay thế bất kỳ * với 0 và 255. Chuyển đổi IP để nguyên

Vì vậy, nếu 255.255.255. * trở thành 255.255.255.0 và 255.255.255.255 Sau đó, thực hiện chức năng ip2long trên hai ips này.

Sau đó, bạn có thể chuyển đổi ip đã cho thành ip dài. ví dụ 255.255.50.51 vào ip dài.

Sau đó, bạn có thể so sánh xem ip dài cho ip đã cho này có nằm giữa ips dài được chuyển đổi trong danh sách đen hay không. Nếu nó là sau đó nó không được phép khác nó được.

$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 cho so sánh ip2long và số thay vì so sánh chuỗi –

+2

IP thứ hai trong danh sách sẽ phá vỡ giải pháp của bạn: '192.168.2.1 *' sẽ trở thành '192.168.2.10' và '192.168.2.1255'. Trong khi người đầu tiên không chính xác những gì chúng tôi muốn, thứ hai là địa chỉ IP hoàn toàn bất hợp pháp :) – Oerd

+1

Không nên '$ ip <= $ givenip' trở thành' $ forip <= $ ip2' để phát hiện phạm vi xxx0 - xxx255? – Lekensteyn

2

Điều này cho phép tất cả các trường hợp trong câu hỏi cộng với mặt nạ ngắn không có dấu hoa thị như 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; 
    } 
} 
Các vấn đề liên quan