CC 攻击往往不是一瞬间开始的,在把网站打挂之前,通常有一段试探期。本文用 9 个问答讲清 CC 攻击爆发前会露出哪些前兆、怎么用命令从日志里提前发现苗头、前兆指标怎么变成可执行的告警规则,以及从「发现异常」到「启动处置」的分级动作。
Q:CC 攻击真的能提前预警吗?提前量有多大?
- 能,但要接受一个事实:没有百分之百的提前预测。预警的价值在于把反应时间从「用户投诉之后」提前到「流量形态刚开始变形时」。
- 大部分 CC 攻击(尤其是有偿攻击与批量压测式攻击)有一个共性:试探期。攻击者会先用小流量测试站点响应、防护阈值与限速策略,确认有效后再加码。
- 这个试探期短则几分钟,长则半小时以上,就是我们做预警的窗口。
- 所以 CC 预警的核心是:识别「形态变化」,而不是等「量变」。等 QPS 明显暴涨时,往往已经进入总攻阶段了。
Q:CC 攻击爆发前有哪些典型前兆?
- 同 IP 请求速率缓慢抬升:不是瞬间爆发,而是从 1r/s 慢慢爬到 5r/s、10r/s,容易被忽略。
- 单个 URL 的请求占比异常集中:平时占比不到 5% 的搜索接口或列表接口,突然占到 30% 以上。
- 动态请求占比上升、缓存命中率下降:这是最灵敏的信号,下一问专门展开。
- 新建连接数上升但活跃连接数没同步涨:说明有大量短连接在被反复打开又丢弃,是脚本探测的典型特征。
- 平均响应时间先于流量上涨:流量看着还没变,后端响应已经从 200ms 抬到 800ms,说明后端已经在被消耗。
- 来源集中度与 UA 集中度同时上升:正常流量来源是散开的,攻击流量往往呈现「同一 UA、同一路径、同一节奏」。
Q:怎么用命令从日志里发现这些前兆?
# 1) 分钟级 QPS,看是不是从平峰缓慢爬升
awk '{print $4}' /var/log/nginx/access.log | cut -d: -f1-3 \
| sort | uniq -c | tail -30
# 2) 单个 URL 的请求占比(看是否高度集中)
total=$(wc -l < /var/log/nginx/access.log)
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -5 \
| awk -v t="$total" '{printf "%6d %.1f%% %s\n", $1, $1/t*100, $2}'
# 3) 单 IP 请求数分布,看尾部是否被拉长
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c \
| awk '{print $1}' | sort -n | uniq -c | tail -20
# 4) UA 集中度
awk -F'"' '{print $6}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
- 重点不是看某个数字有多大,而是看它和昨天同一时刻比,形状是不是变了。攻击的前兆几乎都体现在分布形状上,而不是绝对值上。
- 如果发现「单 IP 请求数分布的尾部被拉长」,说明已经有少数 IP 在承担超额请求,这是试探期最典型的表现。
Q:为什么说缓存命中率是最灵敏的前兆指标?
- CC 的目标是消耗后端(CPU、数据库、连接),所以攻击流量会大量集中在动态请求上。
- 正常用户的请求里静态资源占大多数;攻击流量反过来,几乎不拉静态资源,全打在接口和动态页面上。结果就是:动态请求占比上升,缓存命中率下降,回源率上升。
- 这条链条让缓存命中率与回源率成为「流量还没把服务器打挂,就已经能看出异常」的先行指标。
- 落地建议:把「缓存命中率跌破基线 10% 到 20%,同时动态接口平均响应时间上升 50% 以上」作为一个组合条件来触发预警,比单看 QPS 灵敏得多。
- 注意:缓存命中率下降也可能来自缓存集中过期、发布活动或回源抖动,必须结合来源集中度一起看,不能单独下结论。
Q:怎么把前兆做成可用的告警规则?
# 伪代码:每分钟采样一次,对比同时段基线
current_qps = qps_last_minute()
baseline_qps = same_time_baseline(days=14)
current_ratio = top_url_share_last_minute() # 单 URL 占比
baseline_ratio = same_time_baseline_ratio(days=14)
if current_qps > baseline_qps * 1.5 and current_ratio > baseline_ratio * 2:
alert("疑似 CC 前兆")
- 原则一:每条规则都要有基线。用近 14 天同时段的均值做基线,而不是拍一个绝对数字。
- 原则二:优先用双条件。单一指标容易误报,组合条件(如「QPS 上涨 1.5 倍」且「来源集中度上涨」)能过滤掉大部分噪声。
- 原则三:绝对量与相对量并看。小站 100 QPS 就是异常,大站 1000 QPS 可能才刚起步,相对量才通用。
- 告警要分级:观察级(只记录)、预警级(降速加通知)、紧急级(进入处置流程),避免所有异常都直接升级成电话告警。
Q:Nginx 层能提前预置哪些规则?
# 预置两个档位:平时宽松,告警时切到严格档
limit_req_zone $binary_remote_addr zone=cc_normal:10m rate=20r/s;
limit_req_zone $binary_remote_addr zone=cc_strict:10m rate=5r/s;
# 动态接口单独预置更严的桶,平时不动
limit_req_zone $binary_remote_addr zone=api_strict:10m rate=2r/s;
server {
# 用一行 include 切换生效档位
include /etc/nginx/cc-active.conf;
location ~ ^/(api|search)/ {
limit_req zone=api_strict burst=5 nodelay;
limit_req_status 429;
}
}
- 关键:阈值切换只改一行 include,不要整段重写配置,出问题时回滚也只要一秒。
- 切换前必须
nginx -t通过再 reload,避免配置写错把站点弄挂。 - 限速返回码统一用 429,不要用 503,否则会和真实故障混淆,掩盖问题。
- 如果站点在 CDN 或云 WAF 后面,动态接口更要提前在边缘预置限频,别等请求打到源站才反应。
Q:从「发现苗头」到「启动处置」,动作怎么分级?
- 四级阶梯,逐级升级:记录观察 → 降速 → 人机验证 → 封禁。
- 记录观察:只落盘、只打标签,不动线上流量。前兆阶段默认停在这一级。
- 降速:对可疑路径与可疑 UA 降速,返回 429,正常用户几乎无感。
- 人机验证:对可疑档位触发验证码,验证通过放行。
- 封禁:只有在来源高度集中、行为确认为攻击时才封,且用短时封禁(带 timeout),不做永久黑名单。
- 前兆阶段不要急着封 IP:此时形态还没定型,封禁最容易误伤正常用户。每次升级都要有可回滚的路径,并记录触发原因。
Q:预警系统最容易被哪些情况误报?
- 五类常见误报:真实业务活动(大促、秒杀、投放带来的流量自然上涨)、搜索引擎蜘蛛的大批量抓取、CDN 回源抖动或缓存大面积失效、监控探针与压测流量、合作方 API 的定时批量调用。
- 区分方法:真实活动通常来源分散、静态资源同步上涨;CC 前兆则是来源集中、几乎不拉静态资源。
- 把已知的良性来源(搜索引擎 IP 段、CDN 回源段、公司出口、合作方接口)提前加白,能直接消掉一半误报。
- 给压测与批量任务建立报备流程(来源 IP、时间窗口、并发上限),也能把人为误报压下去。
Q:还有什么补充建议?
- 先建基线再谈预警:没有基线,任何阈值都是拍脑袋。把 QPS、单 URL 占比、缓存命中率、动态响应时间四项的「平时形状」记下来。
- 预警的值班要有人:告警发出来没人看,等于没有预警;夜间至少要有电话级升级路径。
- 预警与处置要闭环:每次预警后复盘是误报还是有效前兆,用结果反向修正阈值,别让规则一成不变。
- 把判断沉淀成文档:什么算前兆、什么算确认、谁来决定升级,写成清单,值班的人才能照着执行。
- 一句话总结:CC 预警不是去预测攻击者的下一步,而是尽早发现「流量形状」变了;把 QPS、单 URL 占比、缓存命中率、动态响应时间四个信号的基线建好,用双条件告警替代单指标告警,再配一套分级处置流程,就能把反应时间从「用户投诉后」提前到「攻击还在试探时」。