最可靠方法是先解析域名获取IP,再校验是否属于RFC定义的内网IP段。需用dns_get_record()兼容IPv6,结合filter_var()验证IP有效性,并手动比对127.0.0.0/8、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、100.64.0.0/10等保留地址段。

PHP判断域名是否为内网地址的可靠方法
直接用 filter_var() + FILTER_VALIDATE_IP 验证解析后的 IP 最稳妥。不能只靠域名字符串匹配(比如 "localhost" 或 "test.local"),因为真实请求可能通过 hosts 绑定、反向代理或 DNS 解析指向内网 IP,而域名本身看不出端倪。
关键步骤是:先 gethostbyname() 或 dns_get_record() 获取 A 记录 IP,再对 IP 做内网段校验。注意 gethostbyname() 不支持 IPv6,若需兼容,优先用 dns_get_record($domain, DNS_A + DNS_AAAA) 并分别处理。
常用内网 IP 段的判断逻辑与代码片段
内网地址不是“黑名单域名”,而是 RFC 1918 / 6598 定义的保留 IP 段。PHP 没有内置函数直接判断,需手动比对。以下覆盖绝大多数情况:
-
127.0.0.0/8(本地回环) 10.0.0.0/8-
172.16.0.0/12(即 172.16–172.31) 192.168.0.0/16-
100.64.0.0/10(CGNAT,越来越常见)
示例函数:
立即学习“PHP免费学习笔记(深入)”;
function isPrivateIp($ip) {
if (!filter_var($ip, FILTER_VALIDATE_IP)) return false;
$ipLong = ip2long($ip);
$ranges = [
['127.0.0.0', '127.255.255.255'],
['10.0.0.0', '10.255.255.255'],
['172.16.0.0', '172.31.255.255'],
['192.168.0.0', '192.168.255.255'],
['100.64.0.0', '100.127.255.255'],
];
foreach ($ranges as [$start, $end]) {
if ($ipLong >= ip2long($start) && $ipLong <= ip2long($end)) {
return true;
}
}
return false;
}
HTTP 请求头中 Host 域名不等于服务端实际访问地址
用户可任意伪造 Host 头,$_SERVER['HTTP_HOST'] 或 $_SERVER['SERVER_NAME'] 不能直接用于安全判断。尤其在 Nginx/Apache 反向代理场景下,真实客户端 IP 来自 X-Forwarded-For,但该头也可被伪造 —— 所以必须结合可信代理配置(如 Nginx 的 set_real_ip_from)和 REMOTE_ADDR 校验。
如果你要过滤“来自内网的请求”,真正该检查的是:$_SERVER['REMOTE_ADDR'](经真实 IP 修复后),而不是域名本身。
- 不要写
if (strpos($_SERVER['HTTP_HOST'], 'local') !== false) - 不要仅依赖
$_SERVER['SERVER_ADDR'](这是服务器监听地址,非客户端) - 若用了 CDN 或 LB,确保已用
real_ip_recursive on(Nginx)或类似机制还原REMOTE_ADDR
使用 cURL 或 file_get_contents 请求第三方域名时的内网拦截
当 PHP 主动发起 HTTP 请求(如调用内部 API),必须防止 SSRF(服务器端请求伪造)。此时不能只过滤域名,而要在发起前解析并校验目标 IP 是否为内网地址。
典型错误写法:if (preg_match('/^(localhost|127\.|10\.|192\.168\.)/i', $url)) { ... } —— 这漏掉 172.16–31、100.64–127,且无法处理 CNAME 解析跳转。
正确做法:
- 用
parse_url($url, PHP_URL_HOST)提取 host - 用
gethostbyname()或dns_get_record()获取 IP(注意超时和异常) - 调用前述
isPrivateIp()判断 - 若解析失败或 IP 为私有地址,拒绝请求并记录告警
特别注意:DNS 解析本身可能被污染或劫持,生产环境建议配合白名单域名或固定 IP 调用。
内网地址过滤的本质是 IP 层校验,不是字符串匹配;所有绕过 DNS 解析、只查 Host 头或 URL 的方案,在真实部署中都容易失效。最易忽略的点是:没处理 IPv6 的 fd00::/8 和 ::1,以及没考虑 gethostbyname() 在 DNS 故障时返回原字符串导致 ip2long() 失败 —— 这些都会让防护形同虚设。











