本文目录导读:

针对PHP项目的反欺诈与黑名单机制,我为您提供一个完整的实现方案,这个系统包含IP、设备、用户等多维度检测。
核心数据表设计
-- 黑名单表
CREATE TABLE blacklist (
id INT AUTO_INCREMENT PRIMARY KEY,
type ENUM('ip', 'user_id', 'device_id', 'phone', 'email', 'card') NOT NULL,
value VARCHAR(255) NOT NULL,
reason VARCHAR(500),
level TINYINT DEFAULT 1, -- 1-5, 5为最高风险
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP NULL,
status TINYINT DEFAULT 1, -- 1有效, 0禁用
UNIQUE KEY uk_type_value (type, value),
INDEX idx_type_status (type, status),
INDEX idx_value (value),
INDEX idx_expires (expires_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 行为日志表
CREATE TABLE behavior_log (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id INT,
ip VARCHAR(45),
device_fingerprint VARCHAR(255),
action VARCHAR(100),
action_params TEXT,
risk_score DECIMAL(5,2) DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_user_action (user_id, action),
INDEX idx_ip (ip),
INDEX idx_device (device_fingerprint),
INDEX idx_created (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 风控规则表
CREATE TABLE risk_rules (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100),
rule_type VARCHAR(50),
conditions JSON, -- 规则条件
action VARCHAR(50), -- deny, review, allow
weight DECIMAL(5,2) DEFAULT 1,
status TINYINT DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
核心反欺诈类
<?php
namespace App\Security;
class AntiFraudManager
{
private $db;
private $cache;
private $config;
public function __construct(\PDO $db, $cache = null, array $config = [])
{
$this->db = $db;
$this->cache = $cache;
$this->config = array_merge([
'max_login_attempts' => 5,
'login_window' => 300,
'max_register_per_ip' => 3,
'register_window' => 3600,
'risk_threshold' => 0.7
], $config);
}
/**
* 检查请求是否安全
*/
public function checkRequest(RequestContext $context): RiskResult
{
$totalRisk = 0;
$reasons = [];
// 1. 黑名单检查
$blacklistCheck = $this->checkBlacklist($context);
if ($blacklistCheck->isBlocked()) {
return new RiskResult(true, $blacklistCheck->getReasons(), 1.0);
}
// 2. 行为分析
$behaviorRisk = $this->analyzeBehavior($context);
$totalRisk += $behaviorRisk['score'];
$reasons = array_merge($reasons, $behaviorRisk['reasons']);
// 3. 设备指纹检查
$deviceRisk = $this->analyzeDevice($context);
$totalRisk += $deviceRisk['score'];
$reasons = array_merge($reasons, $deviceRisk['reasons']);
// 4. 规则引擎
$ruleResults = $this->applyRules($context);
$totalRisk += $ruleResults['score'];
$reasons = array_merge($reasons, $ruleResults['reasons']);
$finalRisk = min($totalRisk, 1.0);
$isBlocked = $finalRisk >= $this->config['risk_threshold'];
// 记录行为日志
$this->logBehavior($context, $finalRisk);
return new RiskResult($isBlocked, $reasons, $finalRisk);
}
/**
* 黑名单检查
*/
private function checkBlacklist(RequestContext $context): RiskResult
{
$checks = [
['type' => 'ip', 'value' => $context->ip],
['type' => 'user_id', 'value' => $context->userId],
['type' => 'device_id', 'value' => $context->deviceId],
['type' => 'phone', 'value' => $context->phone],
['type' => 'email', 'value' => $context->email]
];
$reasons = [];
foreach ($checks as $check) {
if (empty($check['value'])) continue;
$stmt = $this->db->prepare(
"SELECT reason, level FROM blacklist
WHERE type = ? AND value = ? AND status = 1
AND (expires_at IS NULL OR expires_at > NOW())"
);
$stmt->execute([$check['type'], $check['value']]);
if ($row = $stmt->fetch()) {
$reasons[] = "Blacklisted {$check['type']}: {$row['reason']}";
return new RiskResult(true, $reasons, $row['level'] / 5);
}
}
return new RiskResult(false, [], 0);
}
/**
* 行为分析
*/
private function analyzeBehavior(RequestContext $context): array
{
$score = 0;
$reasons = [];
// 检查登录频率
if ($context->action === 'login') {
$attempts = $this->getLoginAttempts($context->ip, $this->config['login_window']);
if ($attempts >= $this->config['max_login_attempts']) {
$score += 0.4;
$reasons[] = "Excessive login attempts: {$attempts}";
}
}
// 检查注册频率
if ($context->action === 'register') {
$registrations = $this->getRegistrationsByIP($context->ip, $this->config['register_window']);
if ($registrations >= $this->config['max_register_per_ip']) {
$score += 0.5;
$reasons[] = "Excessive registrations from same IP";
}
}
// 检查异常时间段
$hour = date('H');
if ($hour >= 1 && $hour <= 5) {
$score += 0.1;
}
return ['score' => min($score, 1.0), 'reasons' => $reasons];
}
/**
* 设备分析
*/
private function analyzeDevice(RequestContext $context): array
{
$score = 0;
$reasons = [];
if (empty($context->deviceFingerprint)) {
return ['score' => 0.1, 'reasons' => ['No device fingerprint']];
}
// 检查设备关联风险用户
$stmt = $this->db->prepare(
"SELECT COUNT(*) as cnt FROM behavior_log
WHERE device_fingerprint = ?
AND risk_score > 0.5
AND created_at > DATE_SUB(NOW(), INTERVAL 24 HOUR)"
);
$stmt->execute([$context->deviceFingerprint]);
$result = $stmt->fetch();
if ($result['cnt'] > 3) {
$score += 0.3;
$reasons[] = "Device associated with risk activities";
}
return ['score' => min($score, 1.0), 'reasons' => $reasons];
}
/**
* 规则引擎
*/
private function applyRules(RequestContext $context): array
{
$totalScore = 0;
$reasons = [];
$stmt = $this->db->query("SELECT * FROM risk_rules WHERE status = 1");
$rules = $stmt->fetchAll();
foreach ($rules as $rule) {
$conditions = json_decode($rule['conditions'], true);
$matched = $this->evaluateConditions($conditions, $context);
if ($matched) {
$totalScore += $rule['weight'] * 0.1;
$reasons[] = "Rule matched: {$rule['name']}";
if ($rule['action'] === 'deny') {
$totalScore = 1.0;
break;
}
}
}
return ['score' => min($totalScore, 1.0), 'reasons' => $reasons];
}
/**
* 添加黑名单
*/
public function addToBlacklist(string $type, string $value, string $reason,
int $level = 3, ?int $expiresInHours = null): bool
{
$expiresAt = $expiresInHours ? date('Y-m-d H:i:s', time() + $expiresInHours * 3600) : null;
try {
$stmt = $this->db->prepare(
"INSERT INTO blacklist (type, value, reason, level, expires_at)
VALUES (?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE reason = VALUES(reason), level = VALUES(level),
expires_at = VALUES(expires_at), status = 1"
);
return $stmt->execute([$type, $value, $reason, $level, $expiresAt]);
} catch (\Exception $e) {
// 记录日志
return false;
}
}
/**
* 获取登录尝试次数
*/
private function getLoginAttempts(string $ip, int $window): int
{
$key = "login_attempts:{$ip}";
if ($this->cache) {
return (int)$this->cache->get($key, 0);
}
$stmt = $this->db->prepare(
"SELECT COUNT(*) FROM behavior_log
WHERE ip = ? AND action = 'login'
AND created_at > DATE_SUB(NOW(), INTERVAL ? SECOND)"
);
$stmt->execute([$ip, $window]);
return (int)$stmt->fetchColumn();
}
}
请求上下文类
<?php
namespace App\Security;
class RequestContext
{
public $ip;
public $userId;
public $deviceId;
public $deviceFingerprint;
public $phone;
public $email;
public $action;
public $userAgent;
public $referer;
public $timestamp;
public function __construct(array $data = [])
{
$this->ip = $data['ip'] ?? $_SERVER['REMOTE_ADDR'] ?? '';
$this->userId = $data['user_id'] ?? null;
$this->deviceId = $data['device_id'] ?? null;
$this->deviceFingerprint = $data['fingerprint'] ?? '';
$this->phone = $data['phone'] ?? '';
$this->email = $data['email'] ?? '';
$this->action = $data['action'] ?? '';
$this->userAgent = $data['user_agent'] ?? $_SERVER['HTTP_USER_AGENT'] ?? '';
$this->referer = $data['referer'] ?? $_SERVER['HTTP_REFERER'] ?? '';
$this->timestamp = $data['timestamp'] ?? time();
}
}
风险结果类
<?php
namespace App\Security;
class RiskResult
{
private $isBlocked;
private $reasons;
private $riskScore;
public function __construct(bool $isBlocked, array $reasons = [], float $riskScore = 0)
{
$this->isBlocked = $isBlocked;
$this->reasons = $reasons;
$this->riskScore = $riskScore;
}
public function isBlocked(): bool
{
return $this->isBlocked;
}
public function getReasons(): array
{
return $this->reasons;
}
public function getRiskScore(): float
{
return $this->riskScore;
}
public function getHttpCode(): int
{
return $this->isBlocked ? 403 : 200;
}
public function toArray(): array
{
return [
'blocked' => $this->isBlocked,
'risk_score' => $this->riskScore,
'reasons' => $this->reasons
];
}
}
中间件使用示例
<?php
namespace App\Middleware;
use App\Security\AntiFraudManager;
use App\Security\RequestContext;
class AntiFraudMiddleware
{
private $fraudManager;
public function __construct(AntiFraudManager $fraudManager)
{
$this->fraudManager = $fraudManager;
}
public function handle($request, $next)
{
// 创建请求上下文
$context = new RequestContext([
'action' => $request->getPath(),
'user_id' => $request->getUserId(),
'device_id' => $request->header('X-Device-ID'),
'fingerprint' => $this->getDeviceFingerprint($request),
'phone' => $request->input('phone'),
'email' => $request->input('email')
]);
// 风控检查
$result = $this->fraudManager->checkRequest($context);
if ($result->isBlocked()) {
return response()->json([
'error' => 'Request blocked',
'reasons' => $result->getReasons(),
'code' => $result->getHttpCode()
], $result->getHttpCode());
}
// 将风险信息注入请求
$request->attributes->set('risk_score', $result->getRiskScore());
return $next($request);
}
private function getDeviceFingerprint($request): string
{
// 使用多个参数生成设备指纹
$components = [
$request->header('User-Agent'),
$request->header('Accept-Language'),
$request->header('Accept-Encoding'),
$request->ip()
];
return md5(implode('|', $components));
}
}
自动清理脚本
<?php
// cron_jobs/cleanup_blacklist.php
// 建议每天运行一次
$stmt = $db->prepare(
"DELETE FROM blacklist WHERE expires_at IS NOT NULL AND expires_at < NOW()"
);
$stmt->execute();
// 清理过期行为日志(保留最近30天)
$stmt = $db->prepare(
"DELETE FROM behavior_log WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY)"
);
$stmt->execute();
配置建议
// config/antifraud.php
return [
'redis' => [
'host' => '127.0.0.1',
'port' => 6379,
'prefix' => 'antifraud:'
],
'rate_limits' => [
'login' => [
'max_attempts' => 5,
'window' => 300 // 5分钟
],
'register' => [
'max_attempts' => 3,
'window' => 3600 // 1小时
],
'api' => [
'max_requests' => 100,
'window' => 60 // 1分钟
]
],
'blacklist' => [
'auto_ban_threshold' => 0.8, // 触发自动加入黑名单的风险分数
'auto_ban_duration' => 24 // 自动封禁小时数
],
'notifications' => [
'high_risk_alert' => true,
'alert_channels' => ['email', 'sms'],
'alert_recipients' => ['admin@example.com']
]
];
这个系统提供了完整的反欺诈功能,包括:
- 多维黑名单:支持IP、用户、设备、手机、邮箱等多个维度
- 行为分析:检测异常登录、注册频率等
- 设备指纹:识别设备关联风险
- 规则引擎:可配置的风控规则
- 自动封禁:达到风险阈值自动加入黑名单
- 缓存优化:支持Redis缓存提高性能
- 可扩展性:易于添加新的检测规则
建议配合Nginx限流、验证码、实名认证等机制一起使用,构建完整的防护体系。