本文目录导读:

- 方法一:检查常见的代理请求头(最基础)
- 方法二:获取用户真实IP并检查IP黑名单(推荐)
- 方法三:检测HTTP请求的“签名”差异(高级)
- 方法四:基于频率和行为的动态限制(非直接识别IP性质)
- 综合建议(实战部署流程)
- 注意事项
在PHP项目中识别和拦截通过代理IP发起的访问请求,通常涉及检测请求头、检查IP黑名单(如已知的代理/数据中心IP段)以及使用第三方API,以下是几种常见且有效的实现方法:
检查常见的代理请求头(最基础)
代理服务器(尤其是透明代理或匿名代理)通常会在HTTP请求头中添加特定的字段,你可以检查这些字段的存在性,但这只能拦截部分非高匿代理(如透明代理)。
<?php
function isUsingProxyByHeaders() {
$proxyHeaders = [
'HTTP_X_FORWARDED_FOR',
'HTTP_X_FORWARDED',
'HTTP_X_REAL_IP',
'HTTP_CLIENT_IP',
'HTTP_X_CLUSTER_CLIENT_IP',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED',
'HTTP_VIA', // 代理服务器常用
'HTTP_X_PROXY_ID',
'HTTP_PROXY_CONNECTION'
];
foreach ($proxyHeaders as $header) {
if (isset($_SERVER[$header]) && !empty($_SERVER[$header])) {
// 某些情况下,这些头可能由合法反向代理(如Nginx)设置,
// 需要确认你的服务器架构是否信任这些头。
return true; // 疑似代理
}
}
return false;
}
// 使用示例
if (isUsingProxyByHeaders()) {
http_response_code(403);
echo "Access denied: Proxy detected.";
exit;
}
注意:
- 如果使用了CDN(Cloudflare)或反向代理(Nginx),
HTTP_X_FORWARDED_FOR等头会被正常添加,会导致误杀,此时需要在Nginx层先清除外来不可信的X-Forwarded-For,或只在没有反向代理时使用该方法。
获取用户真实IP并检查IP黑名单(推荐)
代理IP通常来自数据中心或特定云服务商(如AWS、阿里云、腾讯云、DigitalOcean、Linode等),你可以维护一份已知代理/数据中心IP段列表,或者调用第三方服务查询IP的“风险”属性。
获取客户端真实IP(处理代理链)
<?php
function getClientRealIP() {
$ip = '';
// 优先检查 Cloudflare (如果用了)
if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
$ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
}
// 标准代理头(需确保来源可信,否则易伪造)
elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$ip = trim($ips[0]); // 取第一个(最靠近客户端)
}
elseif (isset($_SERVER['HTTP_X_REAL_IP'])) {
$ip = $_SERVER['HTTP_X_REAL_IP'];
}
elseif (isset($_SERVER['REMOTE_ADDR'])) {
$ip = $_SERVER['REMOTE_ADDR'];
}
// 验证是否为合法IP
if (filter_var($ip, FILTER_VALIDATE_IP)) {
return $ip;
}
return '0.0.0.0';
}
检查IP是否在代理IP黑名单中(本地数据库 + 第三方API)
方式1:本地维护IP段数据库(如ip2location + proxy database)
<?php
// 假设你已经有一个本地数据库或文件包含可疑CIDR(如 103.21.244.0/22)
function isProxyIP($ip) {
$proxyCIDRs = [
'103.21.244.0/22', // Cloudflare?
// 更多数据中心IP段...
];
foreach ($proxyCIDRs as $cidr) {
if (ipInCIDR($ip, $cidr)) {
return true;
}
}
return false;
}
// 辅助函数:判断IP是否在CIDR范围内
function ipInCIDR($ip, $cidr) {
list($subnet, $mask) = explode('/', $cidr);
if (!$mask) $mask = 32;
$ipLong = ip2long($ip);
$subnetLong = ip2long($subnet);
$maskLong = -1 << (32 - $mask);
return ($ipLong & $maskLong) == ($subnetLong & $maskLong);
}
方式2:调用第三方API(推荐,更实时但需付费)
<?php
function isProxyByAPI($ip) {
$apiKey = 'YOUR_API_KEY';
// 以 ipqualityscore 为例
$url = "https://ipqualityscore.com/api/json/ip/{$apiKey}/{$ip}?strictness=1";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
$response = curl_exec($ch);
curl_close($ch);
if ($response) {
$data = json_decode($response, true);
if (isset($data['proxy']) && $data['proxy'] === true) {
return true; // 是代理/VPN
}
// 同时可检查 vpn, tor, crawler 等字段
}
return false;
}
常用第三方代理检测API:
- IP2Location Proxy Database (下载本地数据库)
- ipqualityscore
- AbuseIPDB
- MaxMind (GeoIP2 匿名IP数据库)
- Cloudflare (Threat Intelligence)
检测HTTP请求的“签名”差异(高级)
代理服务器有时会修改客户端的一些行为特征,可以通过对比检测:
- 检查Accept-Language是否合理(代理可能不发送或错误发送)
- 检查时间戳/HTTP头顺序(某些代理会重组请求头)
- 检测WebSocket支持(真实浏览器通常支持,简单爬虫可能不支持)
- JavaScript挑战(CAPTCHA):在关键操作前弹出简单的JavaScript验证(如验证鼠标移动轨迹、加载图片),只有真实浏览器能通过,代理IP脚本几乎无法完成。
简单示例(Js挑战): 页面加载后,前端通过JS发送一个带时间戳和计算结果的请求到后端验证,如果后端没收到这个请求,视为非浏览器行为,进而拒绝。
基于频率和行为的动态限制(非直接识别IP性质)
代理IP通常用于批量请求,可以结合速率限制(Rate Limiting) 和行为异常检测来拦截:
- 同一IP短时间大量请求同一个API
- 请求间隔极其规律(秒级间隔,无随机性)
- 请求的User-Agent单一或异常
- 同时产生大量不同Session但IP相同
使用Redis或内存缓存记录IP请求计数:
<?php
$ip = getClientRealIP();
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$key = "rate_limit:{$ip}";
$current = $redis->incr($key);
if ($current > 60) { // 每分钟60次
http_response_code(429);
echo "Too many requests";
exit;
}
$redis->expire($key, 60);
综合建议(实战部署流程)
- 优先使用CDN(如Cloudflare):Cloudflare会自动过滤大量恶意代理和爬虫,你只需要信任
CF-Connecting-IP头,之后在应用层做简单检测即可。 - Nginx层配合:在Nginx中先清除不可信的
X-Forwarded-For,只保留CDN或内部反向代理设置的版本:# Nginx 配置 real_ip_header CF-Connecting-IP; # 如果用了Cloudflare set_real_ip_from 0.0.0.0/0;
- 在PHP中执行:
- 获取真实IP(从可信来源如
CF-Connecting-IP或REMOTE_ADDR)。 - 检查IP是否在黑名单(本地CIDR列表 + 第三方API做二次验证)。
- 结合速率限制和行为检测。
- 获取真实IP(从可信来源如
最终简化版代码示例:
<?php
$realIP = getClientRealIP(); // 上述函数
// 1. 快速本地黑名单检测
if (isProxyIP($realIP)) {
exit('Proxy detected. 403');
}
// 2. 第三方API检测(非高频请求时调用,或缓存结果)
$cacheKey = "proxy_check:{$realIP}";
$cached = apcu_fetch($cacheKey);
if ($cached === false) {
$isProxy = isProxyByAPI($realIP);
apcu_store($cacheKey, $isProxy, 3600); // 缓存1小时
} else {
$isProxy = $cached;
}
if ($isProxy) {
exit('VPN/Proxy detected. 403');
}
// 3. 速率限制
rateLimit($realIP);
// 正常业务逻辑...
注意事项
- 误杀风险:企业出口的NAT IP可能被误判为代理(例如同一公司多人共用公网IP),可适当降低严格度(如放宽频率限制)。
- 性能:不要对每个请求都调用第三方API,务必使用缓存(Redis / APCu)。
- 法律合规:部分国家/地区对拦截代理IP有注册要求(如防火墙合规),注意查看当地法律。
希望这套方案能帮你有效识别和拦截代理IP的访问!