本文目录导读:

设计一个完善的PHP防刷机制需要从多个层面考虑,这里提供一个全面的防刷设计方案:
基础防刷层(请求频率控制)
IP限流中间件
<?php
class RateLimiter {
private $redis;
private $maxRequests;
private $timeWindow;
public function __construct($redis, $maxRequests = 60, $timeWindow = 60) {
$this->redis = $redis;
$this->maxRequests = $maxRequests;
$this->timeWindow = $timeWindow;
}
/**
* 滑动窗口限流
*/
public function limitByIP($ip) {
$key = "rate_limit:ip:{$ip}:" . time();
$count = $this->redis->incr($key);
if ($count === 1) {
$this->redis->expire($key, $this->timeWindow);
}
if ($count > $this->maxRequests) {
throw new \Exception("请求过于频繁", 429);
}
return true;
}
/**
* 令牌桶算法
*/
public function tokenBucket($ip, $capacity = 100, $refillRate = 10) {
$bucketKey = "bucket:{$ip}";
$tokenKey = "tokens:{$ip}";
$lastRefillKey = "last_refill:{$ip}";
$currentTokens = (float)$this->redis->get($tokenKey);
$lastRefill = (int)$this->redis->get($lastRefillKey);
if ($currentTokens === false) {
$currentTokens = $capacity;
$lastRefill = time();
}
// 计算应补充的令牌
$elapsed = time() - $lastRefill;
$newTokens = min($capacity, $currentTokens + ($elapsed * $refillRate));
$this->redis->multi()
->set($tokenKey, $newTokens)
->set($lastRefillKey, time())
->exec();
if ($newTokens < 1) {
throw new \Exception("请求过于频繁,请稍后再试", 429);
}
return true;
}
}
用户级别防刷
用户行为监控
<?php
class UserAntiSpam {
private $db;
private $redis;
/**
* 用户操作频率控制
*/
public function checkUserAction($userId, $action, $limit = 10, $window = 3600) {
$key = "user:{$userId}:action:{$action}:" . date('Y-m-d-H-i');
// 使用计数器
$count = $this->redis->incr($key);
if ($count === 1) {
$this->redis->expire($key, $window);
}
if ($count > $limit) {
$this->logViolation($userId, $action, 'frequency_exceeded');
return false;
}
return true;
}
/**
* 黑名单机制
*/
public function addToBlacklist($userId, $reason, $duration = 86400) {
$key = "blacklist:user:{$userId}";
$this->redis->setex($key, $duration, json_encode([
'reason' => $reason,
'time' => time()
]));
// 记录到数据库
$this->db->insert('user_blacklist', [
'user_id' => $userId,
'reason' => $reason,
'duration' => $duration
]);
}
/**
* 检查是否在黑名单
*/
public function isBlacklisted($userId) {
$key = "blacklist:user:{$userId}";
return $this->redis->exists($key);
}
}
验证码机制
多重验证码系统
<?php
class CaptchaSystem {
private $redis;
/**
* 生成图片验证码
*/
public function generateImageCaptcha($sessionId) {
$captcha = $this->generateRandomCode(4);
$key = "captcha:{$sessionId}";
// 存储验证码和过期时间
$this->redis->setex($key, 300, $captcha);
// 生成图片
$image = imagecreatetruecolor(120, 40);
// ... 图片绘制代码
return $image;
}
/**
* 行为验证码(点击验证,需要前端配合)
*/
public function verifyBehavior($userId, $behaviorData) {
// 记录用户行为特征
$key = "user:{$userId}:behavior";
$features = [
'mouse_speed' => $behaviorData['mouse_speed'],
'click_delay' => $behaviorData['click_delay'],
'move_pattern' => $behaviorData['move_pattern']
];
// 分析是否是机器人操作
$riskScore = $this->calculateRiskScore($features);
return $riskScore < 0.5; // 分数低于0.5判定为正常
}
/**
* 滑动验证码(需要前端配合)
*/
public function verifySliderCaptcha($sessionId, $sliderData) {
// 记录滑动轨迹时长
$startTime = $this->redis->get("slider:{$sessionId}:start");
$endTime = time() * 1000;
$duration = $endTime - $startTime;
// 人类滑动通常需要1-3秒
if ($duration < 500 || $duration > 5000) {
return false;
}
// 验证滑动轨迹
return true;
}
}
业务逻辑防刷
业务层防刷规则
<?php
class BusinessAntiSpam {
private $db;
private $redis;
/**
* 注册防刷
*/
public function checkRegistration($ip, $phone, $deviceId) {
// IP注册数量限制
$ipKey = "register:ip:{$ip}:" . date('Y-m-d');
$ipCount = $this->redis->incr($ipKey);
if ($ipCount > 5) {
return false; // 单个IP每天最多注册5次
}
// 手机号重复注册检查
$phoneExists = $this->db->query(
"SELECT COUNT(*) FROM users WHERE phone = ?",
[$phone]
)->fetchColumn();
if ($phoneExists > 0) {
return false;
}
// 设备指纹检查
$deviceKey = "register:device:{$deviceId}:" . date('Y-m-d');
$deviceCount = $this->redis->incr($deviceKey);
if ($deviceCount > 3) {
return false; // 单个设备每天最多注册3次
}
return true;
}
/**
* 登录防刷
*/
public function checkLogin($userId, $password, $ip) {
// 密码错误次数限制
$failKey = "login:fail:{$userId}:" . date('Y-m-d-H');
$failCount = $this->redis->incr($failKey);
if ($failCount > 5) {
$this->temporaryLockUser($userId, 30); // 锁定30分钟
return false;
}
// IP异常检测
$ipKey = "login:ip:{$ip}:unusual";
$normalIps = $this->db->query(
"SELECT COUNT(*) FROM login_logs WHERE user_id = ? AND ip = ?",
[$userId, $ip]
)->fetchColumn();
if ($normalIps == 0 && $failCount > 3) {
// 新IP登录且多次失败
return false;
}
return true;
}
/**
* 短信验证码防刷
*/
public function checkSmsCode($phone, $type) {
// 发送频率限制(60秒内只能发1条)
$sendKey = "sms:send:{$phone}:{$type}";
$lastSend = $this->redis->get($sendKey);
if ($lastSend && (time() - $lastSend) < 60) {
return false;
}
// 每天数量限制
$dailyKey = "sms:daily:{$phone}:{$type}:" . date('Y-m-d');
$dailyCount = $this->redis->incr($dailyKey);
if ($dailyCount > 10) {
return false;
}
// 验证码错误次数限制
$verifyKey = "sms:verify:{$phone}:{$type}:fail";
$failCount = $this->redis->incr($verifyKey);
if ($failCount > 5) {
return false;
}
return true;
}
}
全局防护配置
完整的防刷系统配置
<?php
class AntiSpamConfig {
// 防刷策略配置
private static $config = [
'rate_limits' => [
'api' => ['limit' => 60, 'window' => 60], // API 60次/分钟
'login' => ['limit' => 10, 'window' => 60], // 登录 10次/分钟
'register' => ['limit' => 5, 'window' => 3600], // 注册 5次/小时
'sms_send' => ['limit' => 3, 'window' => 3600], // 短信发送 3次/小时
],
'blacklist' => [
'ip_blacklist' => [], // IP黑名单
'user_blacklist' => [], // 用户黑名单
'device_blacklist' => [] // 设备黑名单
],
'behavior' => [
'max_errors' => 5, // 最大错误次数
'lock_duration' => 30, // 锁定时间(分钟)
'captcha_threshold' => 3 // 触发验证码的阈值
]
];
/**
* 全局安全检查
*/
public static function globalCheck($request) {
// 检查IP是否被禁止
$ip = $request->getClientIp();
if (self::isIPBlacklisted($ip)) {
return false;
}
// 检查用户Agent
$userAgent = $request->getUserAgent();
if (self::isSuspiciousUserAgent($userAgent)) {
return false;
}
// 检查请求头
$headers = $request->getHeaders();
if (self::hasAbnormalHeaders($headers)) {
return false;
}
// 响应头添加反爬标记
$response = new Response();
$response->headers->set('X-Robots-Tag', 'noindex, nofollow');
return true;
}
}
// 使用示例
class ExampleController {
public function sensitiveAction(Request $request) {
// 执行防刷检查
if (!AntiSpamConfig::globalCheck($request)) {
return response()->json(['error' => '访问被拒绝'], 403);
}
// 业务逻辑...
}
}
监控与日志
防刷监控系统
<?php
class AntiSpamMonitor {
private $db;
private $elasticsearch;
/**
* 实时监控
*/
public function monitor() {
// 实时流量监控
$currentQPS = $this->getCurrentQPS();
$threshold = 1000;
if ($currentQPS > $threshold) {
$this->triggerAlert("高流量警告", $currentQPS);
}
// 异常检测
$anomalies = $this->detectAnomalies();
foreach ($anomalies as $anomaly) {
$this->handleAnomaly($anomaly);
}
}
/**
* 日志记录
*/
public function logAntiSpamEvent($ip, $event, $details) {
$logEntry = [
'timestamp' => date('Y-m-d H:i:s'),
'ip' => $ip,
'event' => $event,
'details' => json_encode($details)
];
// 记录到数据库
$this->db->insert('security_logs', $logEntry);
// 记录到Elasticsearch用于分析
$this->elasticsearch->index('antispam_events', $logEntry);
}
}
最佳实践建议
使用中间件组合
// 在Laravel或ThinkPHP中注册中间件
protected $middleware = [
GlobalRateLimit::class, // 全局限流
IPWhitelist::class, // IP白名单
SecurityHeaders::class, // 安全头
UserAuthCheck::class, // 用户认证
RequestValidation::class, // 请求验证
];
// 路由分组
Route::group(['middleware' => ['rate_limit:login']], function () {
Route::post('/login', 'AuthController@login');
});
动态调整策略
class AdaptiveAntiSpam {
public function adjustStrategy($userId, $riskLevel) {
switch ($riskLevel) {
case 'low':
$this->setRateLimit($userId, 100, 60);
break;
case 'medium':
$this->setRateLimit($userId, 20, 60);
$this->requireCaptcha(true);
break;
case 'high':
$this->setRateLimit($userId, 5, 3600);
$this->lockAccount($userId);
break;
}
}
}
注意事项
- 性能优化:使用Redis等缓存工具存储计数数据,避免频繁数据库操作
- 误杀处理:设计合理的惩罚机制,避免误杀正常用户
- 分布式支持:在集群环境中,使用Redis共享计数状态
- API设计:提供友好的错误信息,便于前端展示
- 安全更新:定期评估和更新防刷策略
这个防刷系统设计涵盖了多个层面,可以根据实际业务需求进行裁剪和扩展,记得要持续监控效果并定期优化策略。