为何filter_var将所有IP判定为私有/保留地址?代码问题排查
PHP IP地址有效性及私有/保留IP判断函数修复
问题描述
我编写了一个函数,用于判断通过$_SERVER["REMOTE_ADDR"]获取的客户端IP是否有效,以及是否属于私有/保留IP(如127.0.0.1、192.168.1.100等)。但无论是否开启VPN,所有IP都被PHP判定为同时属于私有和保留地址。使用环境为PHP 8.1.8(Win10系统),请问代码存在什么问题?
原代码
function ip($an_ip_address) { $flags = [ "validate" => FILTER_VALIDATE_IP, "ipv4" => FILTER_FLAG_IPV4, "ipv6" => FILTER_FLAG_IPV6, "reserved" => FILTER_FLAG_NO_RES_RANGE, "private" => FILTER_FLAG_NO_PRIV_RANGE, ]; $info = [ "ip" => $an_ip_address, "valid" => false, "type" => "IPv", "reserved" => false, "private" => false, ]; $info["valid"] = filter_var($an_ip_address, $flags["validate"], $flags["ipv4"]|$flags["ipv6"]) !== false; if (!$info["valid"]) { $info["type"] = null; return $info; } // Otherwise $info["type"] .= strval(filter_var($an_ip_address, $flags["validate"], $flags["ipv4"]) !== false ? 4 : 6); // Now let's check if is in private range if (filter_var($an_ip_address, $flags["validate"], $flags["ipv4"]|$flags["private"]) !== false) { // An reserved IPv4 address $info["private"] = true; } else if (filter_var($an_ip_address, $flags["validate"], $flags["ipv6"]|$flags["private"]) !== false) { // An reserved IPv4 address $info["private"] = true; } // Or in reserved range if (filter_var($an_ip_address, $flags["validate"], $flags["ipv4"]|$flags["reserved"]) !== false) { // An reserved IPv4 address $info["reserved"] = true; } else if (filter_var($an_ip_address, $flags["validate"], $flags["ipv6"]|$flags["reserved"]) !== false) { // An reserved IPv4 address $info["reserved"] = true; } return $info; } // Let's check it out foreach (["87.249.132.40","108.36.9.244","122.20.178.25"] as $ip) { $ip = ip($ip); echo json_encode($ip, JSON_PRETTY_PRINT).PHP_EOL; }
原运行结果
{ "ip": "87.249.132.40", "valid": true, "type": "IPv4", "reserved": true, "private": true } { "ip": "108.36.9.244", "valid": true, "type": "IPv4", "reserved": true, "private": true } { "ip": "122.20.178.25", "valid": true, "type": "IPv4", "reserved": true, "private": true }
错误原因分析
问题出在你搞反了两个过滤标志的作用逻辑:
FILTER_FLAG_NO_PRIV_RANGE:传递这个标志时,filter_var会拒绝私有IP——如果输入的是私有IP,返回false;如果是公网IP,返回非false。FILTER_FLAG_NO_RES_RANGE:同理,这个标志会拒绝保留IP——输入保留IP返回false,非保留IP返回非false。
你原来的代码里,把“返回非false”判定为“属于私有/保留IP”,刚好和实际逻辑相反,所以所有公网IP都被错误标记成了私有+保留地址。
修正后的代码
function ip($an_ip_address) { $flags = [ "validate" => FILTER_VALIDATE_IP, "ipv4" => FILTER_FLAG_IPV4, "ipv6" => FILTER_FLAG_IPV6, "reserved" => FILTER_FLAG_NO_RES_RANGE, "private" => FILTER_FLAG_NO_PRIV_RANGE, ]; $info = [ "ip" => $an_ip_address, "valid" => false, "type" => "IPv", "reserved" => false, "private" => false, ]; // 验证IP有效性(支持IPv4/IPv6) $info["valid"] = filter_var($an_ip_address, $flags["validate"], $flags["ipv4"]|$flags["ipv6"]) !== false; if (!$info["valid"]) { $info["type"] = null; return $info; } // 判断IP类型 $info["type"] .= filter_var($an_ip_address, $flags["validate"], $flags["ipv4"]) !== false ? '4' : '6'; // 判断是否为私有IP:加上NO_PRIV_RANGE后返回false,说明IP是私有地址 if (filter_var($an_ip_address, $flags["validate"], $flags["ipv4"]|$flags["private"]) === false) { $info["private"] = true; } else if (filter_var($an_ip_address, $flags["validate"], $flags["ipv6"]|$flags["private"]) === false) { $info["private"] = true; } // 判断是否为保留IP:加上NO_RES_RANGE后返回false,说明IP是保留地址 if (filter_var($an_ip_address, $flags["validate"], $flags["ipv4"]|$flags["reserved"]) === false) { $info["reserved"] = true; } else if (filter_var($an_ip_address, $flags["validate"], $flags["ipv6"]|$flags["reserved"]) === false) { $info["reserved"] = true; } return $info; } // 测试公网IP和私有IP foreach (["87.249.132.40", "192.168.1.100", "127.0.0.1"] as $ip) { $result = ip($ip); echo json_encode($result, JSON_PRETTY_PRINT) . PHP_EOL; }
修正后的运行结果示例
{ "ip": "87.249.132.40", "valid": true, "type": "IPv4", "reserved": false, "private": false } { "ip": "192.168.1.100", "valid": true, "type": "IPv4", "reserved": false, "private": true } { "ip": "127.0.0.1", "valid": true, "type": "IPv4", "reserved": true, "private": true }
内容的提问来源于Stack Exchange,提问作者Nathan
相关产品推荐
相关产品推荐

