多层级 CC 防护体系:前端验证与后端限流的协同配置实践

在抗 CC(Challenge Collapsar)攻击的实战中,只靠后端限流或只靠前端的 JS 弹窗验证都远远不够------前者容易被模拟浏览器的僵尸网络绕过且易拖垮业务,后者面对大规模随机 IP 攻击时会造成前端挑战泛滥、用户体验崩坏。

真正有效的做法,是构建由外向内、逐层过滤的多层级 CC 防护体系 ,让前端验证负责"筛掉低级 bot",后端限流负责"兜底异常流量",两者通过阈值梯度和挑战升级策略形成协同闭环。


一、多层级 CC 防护总体架构

推荐按以下五个层次部署(外 → 内):

复制代码
[用户浏览器]
    ↓
[L1 CDN/边缘节点]  → IP 粗粒度限流 + Geo 封禁 + 简单 JS Challenge
    ↓
[L2 WAF / 接入网关] → 前端验证(JS 计算/ Cookie Challenge / CAPTCHA)+ 精细化限流
    ↓
[L3 应用层/API Gateway] → 接口级令牌桶 / 滑动窗口限流(per-IP / per-User / per-API)
    ↓
[L4 微服务/业务] → 线程池隔离、方法级限流(Sentinel / Hystrix)
    ↓
[L5 后端资源] → 熔断降级(DB 慢查询熔断、队列积压丢弃)

核心原则:

  • 外层(L1/L2)尽可能用低成本方式拦掉自动化工具、扫描器、弱 bot;

  • 内层(L3/L4)做精确的 QPS/并发控制,防止单用户或单接口把后端打死;

  • 各层阈值由外到内递减,避免内层先于外层触发导致误杀或雪崩。


二、L1:CDN / 边缘节点 --- 第一道粗筛

大多数云 CDN(阿里云 DCDN、腾讯云 EO、Cloudflare 等)自带 CC 防护:

  • 单 IP 每秒请求数限制(如 100 QPS/IP/5s),超出直接丢弃或弹出 JS Challenge。

  • 全局 QPS 封顶 + 自动清洗,应对反射型泛洪。

  • UA / Geo / ASN 封锁:封禁常见 scanner UA、不通国家区域。

  • 对搜索引擎 UA(Googlebot/Baiduspider)做正向 DNS 反查验证后加白

✅ 此层不要求 100% 精准,目标是把 60%~80% 的垃圾流量消化在边缘,减轻回源压力。


三、L2:前端验证 + 网关限流(最关键协同层)

这是"前端验证与后端限流协同"的主战场。

1. 前端验证三种形态及适用场景

验证方式 原理 优缺点 适用场景
**JS Challenge(运算挑战)**​ 返回 307/200 含 JS,要求浏览器计算 hash 后携带 Cookie 重访 无感、拦脚本 bot;但高级 headless Chrome 可绕过 默认 CC 挑战,首次访问或触发轻度限流时
**Cookie Challenge(静默种 Cookie)**​ 302 跳转种 cookie,再次请求校验 极轻量;仅防无 cookie 的简单请求 配合 JS Challenge 做二级过滤
CAPTCHA / 滑块验证 人机交互验证,通过后发放 token/session 强人机区分;影响 UX,移动端需注意 持续异常 IP、多次 JS Challenge 失败后升级

协同逻辑:

复制代码
正常用户 → 首次请求 → 触发 JS Challenge(无感)
         → 浏览器完成计算 → 带 Cookie 重新请求 → 放行

Bot / 脚本 → 不带 Cookie / 不执行 JS → 反复被 Challenge 或 403
异常 IP(高频)→ JS Challenge 多次仍不通过 → 升级为 CAPTCHA
仍异常 → L1 CDN 封 IP 一段时间

2. Nginx + Lua(OpenResty)实现 JS Challenge + 限流示例

下面给出一个思路级配置示例(生产需按情况调整):

复制代码
http {
    lua_shared_dict cc_zone 100m;

    server {
        listen 80;
        server_name example.com;

        # 基础 IP 限流:100r/s,突发 200
        limit_req_zone $binary_remote_addr zone=cc_limit:100m rate=100r/s;
        # JS Challenge 标记(记录已通过 Challenge 的 IP:Cookie)
        lua_shared_dict js_done 50m;

        location / {
            # Step1: 检查是否已持有有效 cc_cookie
            access_by_lua_block {
                local cookie = ngx.var.http_cookie
                if cookie and string.find(cookie, "cc_pass=1") then
                    return  -- 已通过挑战,直接进入限流
                end

                -- Step2: 简易计数器,判断是否要弹 JS Challenge(避免每个请求都弹)
                local key = "js:" .. ngx.var.binary_remote_addr
                local dict = ngx.shared.cc_zone
                local cnt, err = dict:incr(key, 1, 0, 60) -- 60s TTL
                if cnt > 10 then  -- 同一 IP 60s 内 >10 请求且未带 cookie → 弹挑战
                    ngx.header['Content-Type'] = 'text/html'
                    ngx.say([[
                        <html><body>
                        <script>
                            // 简单 JS 运算挑战
                            var t = Date.now(); 
                            while(Date.now() - t < 800); // 模拟计算耗时
                            document.cookie="cc_pass=1; path=/; max-age=900";
                            location.reload();
                        </script>
                        <p>Verifying browser...</p>
                        </body></html>
                    ]])
                    ngx.exit(200)
                end
            }

            # Step3: 通过挑战后,执行限流(防绕过)
            limit_req zone=cc_limit burst=200 nodelay;

            proxy_pass http://backend;
        }
    }
}

💡 要点 :只有带 cc_pass=1Cookie 的请求才进入 limit_req,纯脚本直接拿不到 Cookie 就卡在 JS Challenge 层,不会消耗后端限流桶。

3. 云 WAF / 商业网关配置建议

  • 开启**"浏览器检查(Browser Integrity Check)"** ​ + "JS Challenge 自动下发"

  • 设置触发条件:单 IP QPS > N 或 请求特定高频路径(如 /login/search?q=*

  • 配置挑战失败 N 次 → 自动封 IP M 分钟​ 或 弹出 CAPTCHA

  • 将支付回调、微信/支付宝 Notify URL、内部服务 mTLS 调用加入白名单,跳过所有 Challenge


四、L3:应用层/API Gateway --- 精准限流兜底

即使前端验证拦掉了大部分 bot,必须假设前端可被绕过(高级 bot 可模拟 JS 执行),所以应用层要有独立限流:

  • 算法选择 :推荐令牌桶(Token Bucket) 控制平均 QPS,**滑动窗口(Sliding Window)**更公平但稍重。

  • 限流维度组合

    • per IP(防单一出口攻击)

    • per User/UID(登录态,防账号滥用)

    • per API(热点接口如 /api/search单独设低阈值)

  • 示例 --- Nginx limit_req(应用层前置):

    针对登录接口更严格

    limit_req_zone $binary_remote_addr zone=login_limit:10m rate=5r/s;
    location = /api/login {
    limit_req zone=login_limit burst=10 nodelay;
    proxy_pass http://app;
    }

  • Java 应用可用 Guava RateLimiter (单机)或 Sentinel(分布式、支持预热、冷启动、熔断)。

⚠️ 此处阈值应低于外层 WAF 允许通过的预期峰值,作为最后一道数学防线。例如 WAF 放行预计 ≤5000 QPS,应用层设 5500 QPS 熔断。


五、协同配置关键参数设计参考

层级 动作 典型阈值(参考) 触发条件
CDN 边缘 IP 限流 + JS Challenge 100~200 QPS/IP/60s 全局或指定 URI
WAF 网关 JS Challenge → CAPTCHA → 封 IP Challenge 失败 ≥3 次/60s → CAPTCHA;≥5 次 → 封 15min 请求无 Cookie / 高频
应用层 Token Bucket 限流 登录 5 QPS/IP,普通 API 50 QPS/IP 所有通过前层流量
微服务 Sentinel 熔断 RT > 500ms 连续 20 次 → 熔断 10s 下游 DB/RPC 慢

六、常见坑 & 验收建议

避坑:

  • ❌ 对所有接口(含回调/健康检查)开启 JS Challenge → 支付通知永远收不到

    ✅ 配置精确白名单路径

  • ❌ 搜索引擎被 JS Challenge 拦 → SEO 归零

    ✅ 对 Googlebot/Baiduspider 做 IP 反向验证后加白

  • ❌ 分布式多实例应用层限流用本地内存 → 限流效果被 N 倍放大

    ✅ 用 Redis 滑动窗口或 Sentinel 集群流控

  • ❌ Challenge 页面无 noscript 提示 → 禁用 JS 的用户完全无法访问

    ✅ 返回 403 并提示"请启用 JavaScript 或联系客服"

验收:

  1. ab / wrk模拟单 IP 高压请求 → 应见到 JS Challenge 返回,后端无增长。

  2. 模拟通过 Challenge 后继续压 → 应用层 limit_req 应开始 503/429。

  3. 用真实 Googlebot IP(验证正向解析)抓取 → 直接 200,无 Challenge。

  4. 大促前做全链路压测,观察各层拦截量占比(理想:CDN > WAF > App)。


一句话总结

**多层级 CC 防护 = 边缘粗筛(IP 限流/JS Challenge)+ WAF 前端验证(无感 Challenge → CAPTCHA 升级)+ 应用层精确限流兜底。**​

前端验证把成本低的无脑 bot 挡在外面,后端限流保证即便挑战被绕过也不穿透业务------两层通过阈值梯度、挑战升级、白名单机制紧密协同,才能在抗 CC 同时保住真实用户体验。

如需,我也可以根据你用的具体组件(Nginx 原生 / OpenResty / Kong / APISIX / 云 WAF 控制台)给更贴近生产的完整配置片段。

相关推荐
拂尘子2 分钟前
前端屎山代码救星:这个 MCP 把 7000 行页面压成 60 行骨架,Token 直接省掉 90%+
前端·ai编程·mcp
小雨下雨的雨6 分钟前
月相分析工具鸿蒙PC Electron框架技术实现详解
前端·javascript·华为·electron
布依前端13 分钟前
基于 Vue 3 的 Tiptap 富文本编辑器实践:tiptap-editor-vue3 项目介绍
前端·javascript·vue.js
土星云SaturnCloud14 分钟前
从云端到边缘:基于土星云SE110S的智能视频分析轻量化部署方案(上)
服务器·人工智能·ai·边缘计算
阿狸猿15 分钟前
论负载均衡技术在 Web 系统中的应用
运维·前端·负载均衡
Adorable老犀牛19 分钟前
靠Claude Code写了登录助手AuthDash:一键自动登录,每天节省10分钟——这才是AI编程的降本增效
运维·ai编程·claudecode
橘猫走江湖23 分钟前
Cursor Vibe Coding 开发指南
前端
因_崔斯汀31 分钟前
网页为什么需要框架?
前端
前端 贾公子31 分钟前
Tailwind CSS `shrink-0`是啥意思?
前端
浮游本尊42 分钟前
前端vue转后端java学习路径
java·前端·vue.js