douyong2531 2012-03-06 10:58
浏览 61
已采纳

重构基于PHP的IP过滤器以使用IPv6

I have an IP-Filter for a whitelist in one of my older projects, which I want to reuse in a new application.

edit for clarification; It works like this:

The whitelist contains entries in a format specified below. Using foreach ($whitelist as $listed), I check which type the current entry ($listed) is, and then compare this entry with $ip. As soon as it finds an entry, that matches the specified IP, it will return true, if after going through the whole whitelist no match was found it will return false.

As of now, only IPv4 is supported and the filter allows for a whitelist entries like followed:

  1. IP-range by specifying BEGIN - END (192.168.0.1-192.168.0.5)
  2. single IP-address (e.g. 192.168.0.2)
  3. IP-range using a *-wildcard (e.g. 192.168.0.*)

The methods to check each of these cases look like this, where $ip is the client's IP and $listed is an entry from the whitelist/blacklist matching one of the above mentioned formats:

public function checkAgainstRange($ip, $listed)
{
    list($begin, $end) = explode('-', $listed);
    $begin = ip2long($begin);
    $end = ip2long($end);
    $ip = ip2long($ip);
    return ($ip >= $begin && $ip <= $end);
}

public function checkAgainstSingle($ip, $listed)
{
    return long2ip(ip2long($ip)) === long2ip(ip2long($listed));
}

public function checkAgainstWildcard($ip, $listed)
{
    $listedSegment = explode('.', $listed);
    $ipSegment = explode('.', $ip);

    for ($i = 0; $i < count($listedSegment); $i++) {
        // We're finished when the wildcarded block is reached
        // Important: Check for wildcard first, as it won't match the client IP!
        if ($listedSegment[$i] == '*') {
            return true;
        }
        if ($listedSegment[$i] != $ipSegment[$i]) {
            return false;
        }
    }
    // Just to be safe: If we reach this, something went wrong
    return false;
}

I need some directions as to how to make these work with IPv6-addresses.

Some of the required changes are obvious: * ip2long() only works with IPv4-addresses * I have to check for : as a possible delimiter in checkAgainstWildcard().

I found inet_ntop()and inet_pton() in the php-docs. Can I just use the following to compare two single ip-addresses?

public function checkAgainstSingle($ip, $listed)
{
    $ip = inet_ntop($ip);
    $listed = inet_ntop($listed);
    if ($ip === false || $listed === false) {
        throw new \Exception;
    }
    return $ip === $false;
}

Usage examples:

$filter = new IpFilter();
$ip = $_SERVER['REMOTE_ADDR'];

$result = $filter->checkAgainstSingle($ip, '192.168.0.1');
$result = $filter->checkAgainstRange($ip, '192.168.0.1-192.168.0.10');
$result = $filter->checkAgainstWildcard($ip, '192.168.0.*');

Is it even useful to keep something like checkAgainstRange()? And if so, how could I even check for IPv6-ranges in a similar way? Obviously I cant't change ip2long() to inet_ntop() here...

Same goes for wildcard-ranges. Should I keep them and would it suffice to check for : as a segment delimiter and if it is not found, fall back to . as delimiter?

  • 写回答

1条回答 默认 最新

  • dongliping003116 2012-03-06 17:22
    关注

    If the IP address in in human-readable/printable format then you can use inet_pton() to convert it to binary form. Then you can for example use bin2hex() to show the binary data in hexadecimal form.

    Example:

    $address = inet_pton("2a00:8640:1::1");
    echo bin2hex($address);
    

    will show you:

    2a008640000100000000000000000001
    

    If you then want to check on subnet instead of on separate address you can compare the first 16 hexadecimal characters. IPv6 subnets are almost always /64 networks, each hexadecimal character is 4 bits, so the first (64 / 4 =) 16 characters show you the subnet. Remember that with IPv6 a machine can have multiple IPv6 addresses and if it uses privacy extensions (enabled by default on recent Windows and Mac OS X systems) then it will change it's source address regularly. Matching on subnet might be the most useful for IPv6.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 目详情-五一模拟赛详情页
  • ¥15 有了解d3和topogram.js库的吗?有偿请教
  • ¥100 任意维数的K均值聚类
  • ¥15 stamps做sbas-insar,时序沉降图怎么画
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看
  • ¥15 关于#Java#的问题,如何解决?
  • ¥15 加热介质是液体,换热器壳侧导热系数和总的导热系数怎么算
  • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计
  • ¥15 cmd cl 0x000007b
  • ¥20 BAPI_PR_CHANGE how to add account assignment information for service line