本文目录导读:

在PHP项目中实现地域限流(封禁高风险地区访问)通常有以下几种方案:
IP地址库方案
使用IP数据库(推荐)
<?php
class GeoBlock {
private $highRiskRegions = ['XX', 'YY']; // 高风险地区代码
public function checkAccess($ip) {
// 使用IP数据库查询地理位置
$location = $this->getLocation($ip);
if ($location && in_array($location['country_code'], $this->highRiskRegions)) {
return false;
}
return true;
}
private function getLocation($ip) {
// 使用纯真IP库或GeoIP2等
// 示例使用GeoIP2
$reader = new GeoIp2\Database\Reader('/path/to/GeoLite2-Country.mmdb');
try {
$record = $reader->country($ip);
return [
'country_code' => $record->country->isoCode,
'country_name' => $record->country->name
];
} catch (\Exception $e) {
return null;
}
}
}
使用第三方API(适合低并发)
<?php
function checkByApi($ip) {
$apiKey = 'YOUR_API_KEY';
$url = "https://api.ip2location.com/v2/?ip={$ip}&key={$apiKey}&format=json";
$response = file_get_contents($url);
$data = json_decode($response, true);
// 检查是否为高风险地区
$highRiskCodes = ['CN', 'RU', 'KP']; // 示例
if (in_array($data['country_code'], $highRiskCodes)) {
return false;
}
return true;
}
中间件/框架集成方案
Laravel框架示例
<?php
// app/Http/Middleware/GeoBlockMiddleware.php
namespace App\Http\Middleware;
use Closure;
use Stevebauman\Location\Facades\Location;
class GeoBlockMiddleware
{
public function handle($request, Closure $next)
{
$ip = $request->ip();
$position = Location::get($ip);
if ($position && in_array($position->countryCode, ['RU', 'CN', 'KP'])) {
abort(403, 'Access denied from your region');
}
return $next($request);
}
}
Nginx层面封禁(推荐生产环境)
使用ngx_http_geoip_module
# nginx.conf
http {
geoip_country /path/to/GeoIP.dat;
map $geoip_country_code $blocked_country {
default 0;
RU 1;
CN 1;
KP 1;
}
server {
location / {
if ($blocked_country) {
return 403;
}
# 正常逻辑
}
}
}
使用IP集(适合固定IP列表)
# nginx.conf
http {
geo $block_region {
ranges;
default 0;
# 俄罗斯IP段示例
46.0.0.0/8 1;
95.0.0.0/8 1;
# 添加其他IP段
}
server {
if ($block_region) {
return 403;
}
}
}
全面解决方案示例
<?php
class ComprehensiveGeoBlocker {
private $geoDbPath;
private $highRiskRegions;
private $cache;
public function __construct() {
$this->geoDbPath = '/path/to/GeoIP.mmdb';
$this->highRiskRegions = [
'RU' => 'Russia',
'KP' => 'North Korea',
'IR' => 'Iran',
'SY' => 'Syria',
'CU' => 'Cuba'
];
$this->initCache();
}
public function handle() {
$ip = $this->getClientIP();
// 检查缓存
if ($this->isCached($ip)) {
return $this->getCachedResult($ip);
}
$result = $this->checkIP($ip);
$this->setCache($ip, $result);
if (!$result) {
$this->logBlockedAccess($ip);
http_response_code(403);
exit('Access denied');
}
}
private function getClientIP() {
$headers = [
'HTTP_X_FORWARDED_FOR',
'HTTP_X_REAL_IP',
'HTTP_CLIENT_IP',
'REMOTE_ADDR'
];
foreach ($headers as $header) {
if (!empty($_SERVER[$header])) {
$ips = explode(',', $_SERVER[$header]);
$ip = trim($ips[0]);
if (filter_var($ip, FILTER_VALIDATE_IP)) {
return $ip;
}
}
}
return $_SERVER['REMOTE_ADDR'];
}
private function checkIP($ip) {
try {
$reader = new \MaxMind\Db\Reader($this->geoDbPath);
$data = $reader->get($ip);
if ($data && isset($data['country']['iso_code'])) {
$countryCode = $data['country']['iso_code'];
return !in_array($countryCode, array_keys($this->highRiskRegions));
}
return true; // 无法确定地区时放行
} catch (\Exception $e) {
return true; // 出错时放行
}
}
private function initCache() {
// 使用Redis或文件缓存
$this->cache = new \Predis\Client();
}
private function isCached($ip) {
return $this->cache->exists("geo_block:{$ip}");
}
private function getCachedResult($ip) {
return $this->cache->get("geo_block:{$ip}") === 'true';
}
private function setCache($ip, $result) {
$this->cache->setex("geo_block:{$ip}", 3600, $result ? 'true' : 'false');
}
private function logBlockedAccess($ip) {
$logEntry = [
'time' => date('Y-m-d H:i:s'),
'ip' => $ip,
'url' => $_SERVER['REQUEST_URI'],
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? ''
];
file_put_contents(
'/var/log/geo_blocked.log',
json_encode($logEntry) . PHP_EOL,
FILE_APPEND
);
}
}
// 使用
$blocker = new ComprehensiveGeoBlocker();
$blocker->handle();
注意事项
-
性能优化:
- 使用CDN/WAF级别进行地域封禁
- 缓存IP地理位置结果
- 使用内存数据库(Redis)存储黑名单
-
法律合规:
- 确保符合当地数据保护法规
- 披露IP地址使用政策
-
误判处理:
- 保留白名单机制
- 允许用户申诉
-
监控告警:
// 监控封禁情况 if ($blockedCount > $threshold) { sendAlert("大量地域封禁触发,可能误判或遭受攻击"); } -
混合策略:
- 结合CDN(CloudFlare等)的地域封禁功能
- 使用Web应用防火墙(WAF)
- 定期更新IP数据库
推荐方案
对于生产环境,推荐:
- 第一层:CDN/WAF层面的地域封禁(性能最好)
- 第二层:Nginx GeoIP模块(快速有效)
- 第三层:PHP应用层封禁(最灵活但性能损耗较大)
根据项目规模和预算选择合适的方案组合。