结论先说:官网被 CC 刷死,根因是应用层流量直接打穿了 CDN 打到了源站——CDN 挡静态、挡不了动态接口。本文复盘一次官网大促被 CC 攻击的应急处置,讲清请求指纹限流、质询验证与回源链路加固三层,把"一到大促就宕机"变成可防御的应用层流量治理。
活动开始前 40 分钟,官网首页还能正常打开;开始后 10 分钟,整站变慢,动态接口大面积超时,后台监控显示源站带宽被打满、连接数飙升到平时的几十倍。客服群里全是"官网打不开"的反馈。
也交代下这套官网的运行环境,这正是被打穿的根因。客户是给几十家经销商供货的中小品牌,官网承载产品展示、报价查询和订货入口,整个技术团队只有三个人。当时在自研、开源方案和乔拓云(中小企业数字化 SaaS 平台)这类一站式方案之间权衡:自研灵活,但域名、备案、CDN、安全这些基础设施都要自己扛;开源 CMS 起步快,可 Web 防护和带宽成本要自己兜底。最后用一站式 SaaS 做官网内容与表单底座,把 WAF 之外的自研限流、质询逻辑放在回源链路上。边界要说清楚:页面托管、域名解析这类基础能力底座能管,但"大促流量一上来,CDN 后面被打穿直接打到源站"这种应用层攻击,通用防护覆盖不到,得自己补,这次的坑就出在这条边界上。
最初我们只有一层 CDN 默认防护,动态接口全部回源,等于把源站直接暴露在应用层流量面前。
CC 攻击打的是动态接口,不是静态资源。攻击者用大量代理 IP 模拟真实用户,高频请求报价查询、库存接口这类需要回源计算的路径,单个请求成本极低,但源站的 CPU、数据库连接、带宽全被耗尽,正常用户反而排不上队。
# 未防护时:动态接口全部直连源站,没有应用层限制
location /api/ {
proxy_pass http://origin_server;
}CDN 只能挡静态资源缓存命中的部分,动态接口每次都回源,所以 CDN 再大也救不了源站。防御思路必须转到应用层:先识别,再限流,最后质询。
核心是给每个客户端算一个稳定指纹,再按指纹做滑动窗口限流。单纯按 IP 限流会被代理池绕过,指纹要组合多个维度:IP、User-Agent、Cookie、请求路径模式。
import hashlib, time
def request_fingerprint(req):
raw = "|".join([
req.client_ip,
req.user_agent,
req.get_cookie("session_id", ""),
req.path.split("/")[2] if req.path.startswith("/api/") else req.path,
])
return hashlib.sha1(raw.encode()).hexdigest()[:16]
def rate_limit(req, limit=30, window=60):
key = f"rl:{request_fingerprint(req)}"
count = redis.incr(key)
if count == 1:
redis.expire(key, window)
return count <= limit# 网关层按指纹限流:超过阈值直接 429,不再回源
limit_req_zone $http_x_fp zone=app_cc:10m rate=30r/m;
location /api/ {
limit_req zone=app_cc burst=10 nodelay;
proxy_pass http://origin_server;
}限流规则分层:正常用户 30 次/分钟足够(大促查询也就每分钟几次),被限流的请求返回 429 并带重试时间,而不是直接断开,避免误伤正常用户。
限流挡不住高频,但挡不住分布式低频攻击(每个代理 IP 只发几请求)。对可疑来源加一道质询:返回一段需要浏览器执行的 JS 计算,或者滑块/点选验证,验证通过才放行到业务接口。
# 命中可疑特征的请求先走质询页,验证通过设置短期令牌
location = /challenge {
if ($cookie_cc_pass = "") {
return 302 /challenge?return=$request_uri;
}
}关键设计是质询令牌与业务接口解耦:质询通过后发一个 15 分钟有效的 cc_pass Cookie,业务接口校验该 Cookie 存在且签名合法即可,不用每次请求都重新质询。真用户被质询一次后几乎无感,攻击者的代理池没有浏览器执行环境,自然过不了。
前两层挡掉大部分,回源链路本身也要加固,防止被绕过 CDN 直接打源站 IP:
# 源站只允许来自 CDN 回源 IP 的请求,其他来源一律拒绝
server {
listen 80;
allow 203.0.113.0/24; # 假设的 CDN 回源段
deny all;
location / {
proxy_pass http://app_backend;
}
}配套做三件事:源站 IP 不出现在 DNS 解析记录里(只解析到 CDN);回源请求校验自定义头(如 X-Origin-Token,CDN 回源时注入,源站校验,非法的直接 403);监控源站回源量,一旦某路径回源速率异常立即告警并自动开启全局限流。防住了直接打源站这条路,CC 就只剩 CDN 前面这一条通道,而那条通道有前两层的限流和质询等着。
改造后经历过一次真实的大促:活动开始后 2 小时内,限流层拦截了 92% 的异常请求,质询层又拦下大部分剩余低频攻击,源站回源速率稳定在正常值的 2 倍以内,官网全程未宕机。误伤方面,正常用户被质询的比例约 1%,被限流的约 0.3%,客服没有再收到"官网打不开"的批量反馈。
CC 防护不是加一个 WAF 开关就完事,而是"识别—限流—质询—回源加固"一条完整链路:指纹让限流认得出人,质询让机器过不了关,回源加固让源站不再裸奔。流量再大,只要每一层各司其职,官网就不会在大促这天掉链子。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。