2

我的一个旧项目中有一个用于白名单的 IP 过滤器,我想在新应用程序中重用它。

编辑澄清;它是这样工作的:

白名单包含以下指定格式的条目。使用foreach ($whitelist as $listed),我检查当前条目 ( $listed) 是哪种类型,然后将此条目与$ip. 一旦找到与指定 IP 匹配的条目,它将返回 true,如果在遍历整个白名单后未找到匹配项,它将返回 false。

截至目前,仅支持 IPv4,过滤器允许以下白名单条目:

  1. 192.168.0.1-192.168.0.5通过指定 BEGIN - END ( )指定 IP 范围
  2. 单个 IP 地址(例如192.168.0.2
  3. 使用 * 通配符的 IP 范围(例如192.168.0.*

检查每种情况的方法如下所示,其中$ip是客户端的 IP,并且$listed是与上述格式之一匹配的白名单/黑名单中的条目:

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;
}

我需要一些关于如何使这些与 IPv6 地址一起工作的指导。

一些必要的更改是显而易见的: *ip2long()仅适用于 IPv4 地址 * 我必须:检查checkAgainstWildcard().

我在 php-docs 中找到了inet_ntop()和。inet_pton()我可以使用以下内容来比较两个单个 IP 地址吗?

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

使用示例:

$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.*');

保留类似的东西是否有用checkAgainstRange()?如果是这样,我怎么能以类似的方式检查 IPv6 范围?显然我不能换ip2long()inet_ntop()这里...

通配符范围也是如此。我应该保留它们吗?作为段分隔符检查是否足够:,如果找不到,则回退到.分隔符?

4

1 回答 1

2

如果 IP 地址是人类可读/可打印格式,那么您可以使用 inet_pton() 将其转换为二进制形式。然后,您可以例如使用 bin2hex() 以十六进制形式显示二进制数据。

例子:

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

将向您展示:

2a008640000100000000000000000001

如果您想检查子网而不是单独的地址,您可以比较前 16 个十六进制字符。IPv6 子网几乎总是 /64 网络,每个十六进制字符为 4 位,因此前 (64 / 4 =) 16 个字符显示子网。请记住,对于 IPv6,一台机器可以有多个 IPv6 地址,如果它使用隐私扩展(在最近的 Windows 和 Mac OS X 系统上默认启用),那么它会定期更改它的源地址。子网匹配可能对 IPv6 最有用。

于 2012-03-06T17:22:40.270 回答