当前位置:首页 > 云谷精选

基于PHP的防CC攻击代码实现:动态令牌与请求频率控制

admin2026年03月19日云谷精选41.94万
摘要:# 别让CC攻击拖垮你的网站:一个PHP程序员的实战笔记 我最近帮朋友处理了一个网站,上线不到一周,CPU就飙到100%了。登录服务器一看,日志里全是同一个IP的请求,每秒钟几十次,就盯着登录接口和搜索页面打。 “这不就是典型的CC攻击吗?”我跟他说。…

别让CC攻击拖垮你的网站:一个PHP程序员的实战笔记

我最近帮朋友处理了一个网站,上线不到一周,CPU就飙到100%了。登录服务器一看,日志里全是同一个IP的请求,每秒钟几十次,就盯着登录接口和搜索页面打。

“这不就是典型的CC攻击吗?”我跟他说。

他一脸茫然:“我装了防火墙啊,怎么没用?”

我叹了口气——很多所谓的防护方案,PPT上吹得天花乱坠,真被打的时候就露馅了。尤其是那些用现成面板的,默认配置根本挡不住稍微聪明点的攻击。

今天我就聊聊,怎么用PHP自己实现一套防CC的机制。说白了,就是不给攻击者“舒服”的机会。

一、CC攻击到底在打什么?

很多人觉得CC攻击就是“疯狂刷新页面”,其实没那么简单。

我见过一个电商网站,攻击者专门盯着商品详情页打——每个请求都带不同的商品ID,看起来就像真实用户在浏览。传统的IP封禁根本没用,因为攻击者用了代理池,IP一直在变。

真正的CC攻击,打的是你的业务逻辑瓶颈。

比如:

  • 登录接口(需要查数据库验证)
  • 搜索功能(涉及模糊查询和排序)
  • 数据导出(消耗大量CPU和内存)
  • 验证码生成(GD库操作)

这些地方往往没有缓存,每次请求都要走完整业务流程。攻击者只要找到一两个这样的接口,用几百个代理同时请求,你的服务器就撑不住了。

二、动态令牌:让攻击者“对不上暗号”

这是我个人最喜欢的一招,简单、有效、几乎零成本

原理很简单:在页面加载时,生成一个随机的令牌(token),藏在表单或者JavaScript变量里。用户提交请求时,必须带上这个令牌,服务器验证通过才处理。

听起来像CSRF防护?没错,思路类似,但这里我们玩得更“花”一点。

// 生成动态令牌
session_start();

function generate_cc_token() {
    // 加个时间因子,每分钟变一次
    $minute = floor(time() / 60);
    $seed = $_SERVER['REMOTE_ADDR'] . '|' . $minute . '|' . mt_rand(1000, 9999);

    // 用HMAC确保不能被伪造
    $token = hash_hmac('sha256', $seed, '你的密钥别用这个啊');

    // 存到session,同时设置过期时间(比如5分钟)
    $_SESSION['cc_token'] = $token;
    $_SESSION['cc_token_expire'] = time() + 300;

    return $token;
}

// 验证令牌
function verify_cc_token($input_token) {
    if (empty($_SESSION['cc_token']) || empty($_SESSION['cc_token_expire'])) {
        return false;
    }

    // 先检查过期
    if (time() > $_SESSION['cc_token_expire']) {
        unset($_SESSION['cc_token'], $_SESSION['cc_token_expire']);
        return false;
    }

    // 防止时序攻击
    if (!hash_equals($_SESSION['cc_token'], $input_token)) {
        // 这里可以记录失败次数,超过阈值就封IP
        log_failed_attempt($_SERVER['REMOTE_ADDR']);
        return false;
    }

    // 验证成功后,立即刷新令牌(一次一用)
    unset($_SESSION['cc_token'], $_SESSION['cc_token_expire']);
    return true;
}

这招妙在哪?

攻击者用脚本抓取页面时,确实能拿到令牌。但问题是:这个令牌每分钟都在变,而且每个IP的令牌都不一样。攻击者要么实时解析页面(增加成本),要么用固定的令牌(立刻被拒绝)。

我去年给一个论坛加了这套机制,CC攻击的流量直接降了90%。攻击者发现“性价比”太低,转头去找更软的柿子了。

三、请求频率控制:别让“热情用户”烧了服务器

动态令牌防的是自动化脚本,但如果攻击者真的用大量代理人工操作呢?

这时候就需要频率控制了。但注意——别用那种“一分钟最多60次”的粗暴限制,会把真实用户也挡在外面。

我推荐分层控制:

class RequestThrottler {
    private $redis; // 用Redis存计数,比MySQL快得多

    public function __construct() {
        $this->redis = new Redis();
        $this->redis->connect('127.0.0.1', 6379);
    }

    // 第一层:IP基础频率检查
    public function check_ip_rate($ip, $limit_per_minute = 120) {
        $key = "ip_rate:{$ip}";
        $current = $this->redis->incr($key);

        if ($current == 1) {
            // 第一次设置,60秒过期
            $this->redis->expire($key, 60);
        }

        if ($current > $limit_per_minute) {
            // 超过阈值,加入临时黑名单(5分钟)
            $blacklist_key = "ip_blacklist:{$ip}";
            $this->redis->setex($blacklist_key, 300, 'over_limit');
            return false;
        }

        return true;
    }

    // 第二层:用户行为分析(如果有登录态)
    public function check_user_behavior($user_id, $action) {
        $key = "user_action:{$user_id}:{$action}";
        $window_seconds = 300; // 5分钟窗口

        // 使用滑动窗口算法
        $now = time();
        $window_start = $now - $window_seconds;

        // 清理旧记录(这里用有序集合实现)
        $this->redis->zremrangebyscore($key, 0, $window_start);

        // 添加当前请求
        $this->redis->zadd($key, $now, $now . ':' . microtime(true));

        // 设置过期时间
        $this->redis->expire($key, $window_seconds + 10);

        // 检查窗口内请求数
        $count = $this->redis->zcard($key);

        // 不同动作设置不同阈值
        $limits = [
            'login' => 5,    // 5分钟内最多登录5次
            'comment' => 30, // 5分钟最多30条评论
            'search' => 60   // 5分钟最多60次搜索
        ];

        return $count <= ($limits[$action] ?? 30);
    }

    // 第三层:关键接口额外保护
    public function protect_critical_api($identifier) {
        $key = "critical_api:{$identifier}";

        // 使用令牌桶算法
        $tokens = $this->redis->get($key);
        $last_update = $this->redis->get($key . ":time");

        $now = time();
        $max_tokens = 10; // 桶容量
        $refill_rate = 1; // 每秒补充1个令牌

        if ($tokens === false) {
            $tokens = $max_tokens;
            $last_update = $now;
        } else {
            // 计算应该补充多少令牌
            $time_passed = $now - $last_update;
            $tokens_to_add = $time_passed * $refill_rate;
            $tokens = min($max_tokens, $tokens + $tokens_to_add);
        }

        if ($tokens < 1) {
            // 没令牌了,拒绝请求
            return false;
        }

        // 消耗一个令牌
        $tokens -= 1;
        $this->redis->setex($key, 60, $tokens);
        $this->redis->setex($key . ":time", 60, $now);

        return true;
    }
}

这里有几个关键点:

  1. 别只盯着IP——现在谁没有几十个代理IP?要结合用户ID、设备指纹(如果有)一起判断。

  2. 不同接口区别对待:登录接口要严格(防止撞库),文章浏览可以宽松些。

  3. 给真实用户留余地:突然的流量爆发不一定是攻击,可能是你的文章上热搜了。这时候可以动态调整阈值,或者走验证码流程,而不是直接封禁。

四、那些“坑”我帮你踩过了

1. Session的坑

如果你的网站用了负载均衡,session可能不在当前服务器。这时候可以用Redis存令牌,但要注意网络延迟。

我个人的做法是:关键接口用Redis,普通页面用加密的Cookie。Cookie里存{token}|{expire}|{签名},服务器只需要验证签名和过期时间,不用查数据库。

2. 验证码的“双刃剑”

很多人一上来就加验证码,其实验证码很伤用户体验

我的建议是:

  • 第一次超过阈值:记录但不拦截
  • 第二次超过:弹出滑动验证(比如Geetest)
  • 第三次超过:才出数字字母验证码
  • 继续超过:直接拒绝,返回429状态码

3. 别忽略合法爬虫

Googlebot、Baiduspider这些你得放行。可以在User-Agent里识别,但更保险的是验证它们的IP段(官方会公布)。

$allowed_crawlers = [
    'googlebot' => ['64.68.90.', '66.249.64.'], // Google的IP段
    'baiduspider' => ['180.76.', '220.181.'],
    'bingbot' => ['207.46.13.'],
];

function is_legitimate_crawler($ip, $ua) {
    global $allowed_crawlers;

    foreach ($allowed_crawlers as $name => $prefixes) {
        if (stripos($ua, $name) !== false) {
            foreach ($prefixes as $prefix) {
                if (strpos($ip, $prefix) === 0) {
                    return true;
                }
            }
        }
    }

    return false;
}

五、一个完整的实战例子

假设你有个登录接口正在被CC攻击,可以这样加固:

// login.php
require_once 'RequestThrottler.php';
require_once 'CCProtection.php';

$throttler = new RequestThrottler();
$cc = new CCProtection();

// 1. 先检查IP是否在黑名单
if ($throttler->is_ip_blacklisted($_SERVER['REMOTE_ADDR'])) {
    http_response_code(429);
    echo json_encode(['error' => '请求过于频繁,请稍后再试']);
    exit;
}

// 2. 检查动态令牌(如果是页面提交)
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $token = $_POST['cc_token'] ?? '';
    if (!$cc->verify_token($token)) {
        // 令牌无效,可能是攻击,记录并返回错误
        log_suspicious_activity($_SERVER['REMOTE_ADDR'], 'invalid_token');
        http_response_code(403);
        echo json_encode(['error' => '请求无效,请刷新页面重试']);
        exit;
    }
}

// 3. 检查登录频率
if (!$throttler->check_user_behavior(
    $_POST['username'] ?? 'anonymous', 
    'login'
)) {
    // 频率过高,需要验证码
    if (!verify_captcha($_POST['captcha'] ?? '')) {
        echo json_encode([
            'error' => '需要验证码',
            'captcha_required' => true,
            'captcha_url' => '/captcha.php?t=' . time()
        ]);
        exit;
    }
}

// 4. 到这里才是真正的登录逻辑
// ... 验证用户名密码 ...

最后说几句实话

没有任何一种防护是100%有效的。攻击技术在进化,防护手段也得跟着变。

我见过最“绝”的攻击,是模拟真实用户的点击流:先访问首页,等3秒,点击文章,滚动阅读30秒,然后评论……这种高级CC,光靠频率控制是防不住的。

这时候就需要更复杂的方案了——比如用户行为分析、机器学习模型判断。但那是另一个话题了。

对于大多数中小网站来说,今天讲的动态令牌+分层频率控制,已经能挡住90%的CC攻击了。关键是别偷懒,根据自己业务特点调整参数。

你的网站有没有被CC攻击过?用了什么防护方案?欢迎在评论区聊聊——说不定你的经验正好能帮到别人。

好了,代码拿走,记得改密钥和配置。有问题评论区见。

扫描二维码推送至手机访问。

版权声明:本文由www.ysyg.cn发布,如需转载请注明出处。

本文链接:http://www.ysyg.cn:80/?id=770

“基于PHP的防CC攻击代码实现:动态令牌与请求频率控制” 的相关文章

高频CC攻击:你以为限频就能解决?别天真了

# 高频CC攻击:你以为限频就能解决?别天真了 做网站、搞游戏、开API的,没几个不怕CC攻击的。尤其是那种高频CC,上来就是每秒几千几万次请求,不跟你讲道理,目的就一个:用最少的成本,把你的服务器拖到死机。很多人第一反应是“我上个限频策略不就行了?”,…

分析高防系统中的黑洞路由自动触发算法与解除恢复机制

# 当攻击来袭时,你的服务器真的被“黑洞”吸走了吗? 我自己接触过不少刚遭遇DDoS攻击的站长,发现一个挺有意思的现象:很多人一听说服务器进了“黑洞”,第一反应是懵的——“啥玩意儿?我数据呢?网站是不是没了?” 紧接着就是对着服务商一顿催:“赶紧给我放出…

探究基于语义分析的攻击检测算法:识别隐藏在正常请求中的恶意载荷

# 当攻击穿上“隐身衣”:揪出藏在正常请求里的真家伙 我前两天帮一个做电商的朋友看后台日志,那叫一个头疼。流量看着挺正常,下单、加购、浏览,啥都有。可服务器CPU时不时就飙到100%,订单系统动不动就卡死。查了半天,你猜怎么着?那些看起来规规矩矩的“用户…

分析高防CDN的边缘侧SSL握手加速算法对算力消耗的优化

# 边缘握手加速:高防CDN里那个“看不见”的算力魔术 不知道你有没有遇到过这种情况——明明上了高防CDN,网站安全是稳了,可一到流量高峰,页面加载速度还是慢得让人抓狂。这时候你去看监控,CPU和内存占用也没爆表,但用户体验就是上不去。 我去年帮一个电…

探究多维度流量清洗算法:如何过滤非标准协议的异常封包

# 流量清洗那点事儿:当“非标”封包来敲门 我前两天刚翻过一个客户的日志,那场面,简直了。 凌晨三点,报警短信跟催命似的响。登录控制台一看,好家伙,每秒几十万包,协议字段长得稀奇古怪,什么自定义的Flag位、乱改的TTL值、Payload里塞满毫无意义…

深度解析令牌桶与漏桶算法在CDN边缘节点限速中的应用差异

# 令牌桶和漏桶,CDN限速的“油门”和“刹车”到底怎么选? 前两天跟一个做电商的朋友聊天,他愁眉苦脸地说:“促销那会儿,CDN流量费用直接爆了,后台一看,全是爬虫在那儿疯狂薅商品详情页,跟不要钱似的。” 我问他:“你没做限速吗?” 他一脸无奈:“做…