PHP 防刷机制设计

wen PHP项目 3

本文目录导读:

PHP 防刷机制设计

  1. 基础防刷层(请求频率控制)
  2. 用户级别防刷
  3. 验证码机制
  4. 业务逻辑防刷
  5. 全局防护配置
  6. 监控与日志
  7. 最佳实践建议
  8. 注意事项

设计一个完善的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;
        }
    }
}

注意事项

  1. 性能优化:使用Redis等缓存工具存储计数数据,避免频繁数据库操作
  2. 误杀处理:设计合理的惩罚机制,避免误杀正常用户
  3. 分布式支持:在集群环境中,使用Redis共享计数状态
  4. API设计:提供友好的错误信息,便于前端展示
  5. 安全更新:定期评估和更新防刷策略

这个防刷系统设计涵盖了多个层面,可以根据实际业务需求进行裁剪和扩展,记得要持续监控效果并定期优化策略。

抱歉,评论功能暂时关闭!