PHP 中检查或过滤 IP 地址

网络环境异常复杂,有时候我们不得不禁止一些恶意用户访问,禁止的方式有很多种,其中一种就是通过 IP 来限制,本文提供的方法允许你通过 IP 区间、CIDR (Classless Inter-Domain Routing)及单个 IP 格式来检查或过滤 IP 地址。

你可以通过增加一个配置文件,然后将需要禁止的一些 IP 地址通过一定规则添加到配置文件中,在程序初始化的时候,读取配置文件中的每个规则,然后通过本文提供的方法去检查当前访问的客户端 IP 地址是否存在于这些规则中,如果存在,则拒绝提供服务。

<?php
/**
 * PHP 中检查或过滤 IP 地址
 * 
 * 支持 IP 区间、CIDR(Classless Inter-Domain Routing)及单个 IP 格式
 * 整理:http://www.CodeBit.cn
 * 参考:
 *   - {@link http://us2.php.net/manual/zh/function.ip2long.php#70055}
 *   - {@link http://us2.php.net/manual/zh/function.ip2long.php#82397}
 * 
 * @param string $network 网段,支持 IP 区间、CIDR及单个 IP 格式
 * @param string $ip 要检查的 IP 地址
 * @return boolean
 */
function netMatch($network, $ip) {

    $network = trim($network);
    $ip = trim($ip);
    
    $result = false;
    
    // IP range : 174.129.0.0 - 174.129.255.255
    if (false !== ($pos = strpos($network, "-"))) {
        $from = ip2long(trim(substr($network, 0, $pos)));
        $to = ip2long(trim(substr($network, $pos+1)));
        
        $ip = ip2long($ip);
        
        $result = ($ip >= $from and $ip <= $to);

    // CIDR : 174.129.0.0/16
    } else if (false !== strpos($network,"/")) {
        list ($net, $mask) = explode ('/', $network);
        $result = (ip2long($ip) & ~((1 << (32 - $mask)) - 1)) == ip2long($net); 

    // single IP
    } else {
        $result = $network === $ip;
    
    }
    
    return $result;
}

// 174.129.0.0 - 174.129.255.255
var_dump(netMatch(' 174.129.0.0  -  174.129.255.255 ', '174.129.1.31')); // True
var_dump(netMatch(' 174.129.0.0/16 ', '174.139.1.31')); // False
var_dump(netMatch(' 174.129.1.32 ', '174.129.1.31')); // False
?>

由于中国使用的大多数都是动态 IP 地址,所以通过 IP 地址限制访问具有一定的局限性,使用的时候需要谨慎,但是对于应急限制访问来说,还是非常有用的。

摘自 phpBB 的 IP 编码和解码函数

常见的 IP 地址是由 3 个 "." 分隔的 4 组数字共 15 位字符组成,用 phpBB 的编码函数可以将其转换为 8 位字符,长度缩短了近 50%,经过这样转换后,在比较和查询时,性能必定有很大程度的优化。在显示的时候,可以很方便的通过解码函数,将其还原成常见的 IP 格式。

 
<?php
// 说明:摘自 phpBB 的 IP 编码和解码函数
// 整理:http://www.CodeBit.cn

function encode_ip($dotquad_ip)
{
	$ip_sep = explode('.', $dotquad_ip);
	return sprintf('%02x%02x%02x%02x', $ip_sep[0], $ip_sep[1], $ip_sep[2], $ip_sep[3]);
}

function decode_ip($int_ip)
{
	$hexipbang = explode('.', chunk_split($int_ip, 2, '.'));
	return hexdec($hexipbang[0]). '.' . hexdec($hexipbang[1]) . '.' . hexdec($hexipbang[2]) . '.' . hexdec($hexipbang[3]);
}

$ip = encode_ip( '163.56.123.25' );
echo $ip . "<br />";
echo decode_ip($ip) . "<br />";

?>