API 接口没有页面缓存兜底,每个请求都直接打到数据库和业务逻辑,是 CC 攻击最常盯上的目标。本文用问答方式解答 API 被 CC 的典型表现、Nginx 限频配置、限频 key 怎么选、签名防重放怎么做、验证码放哪一层,以及高防 WAF 在接口防护中的实际作用。
Q:API 接口为什么特别容易被 CC 攻击打垮?
和普通网页相比,API 天然是 CC 攻击的优质靶子:
- 没有缓存层兜底:页面可以静态化、CDN 缓存,API 每个请求都要实时计算、查库。
- 单请求成本高:一个统计接口可能触发多表联查,攻击者用小流量就能放大成大资源消耗。
- 认证凭据可复用:token 一旦泄露或无鉴权,攻击者可无限重放。
- 响应格式单一:攻击者容易判断请求是否成功,自动调整攻击参数。
Q:API 被打 CC 时有哪些典型表现?
盯住这几个信号:
- 某接口 QPS 突然超过日常基线数倍,但业务量没有对应增长。
- 数据库连接池打满、慢查询激增,接口响应时间从几十毫秒飙到几秒。
- 带宽不一定高——CC 常是小包高频,流量图正常但 CPU/DB 先崩。
- 日志里大量同 UA、空 Referer 或固定来源 IP 段的重复请求。
快速定位是哪个接口被打:
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
Q:Nginx 层怎么给 API 做限频?
基础配置是 IP 维度的请求限速 + 连接数限制,双管齐下:
limit_req_zone $binary_remote_addr zone=api_ip:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=api_conn:10m;
server {
location /api/ {
limit_req zone=api_ip burst=20 nodelay;
limit_conn zone=api_conn 10;
limit_req_status 429;
proxy_read_timeout 10s;
...;
}
}
关键参数含义:rate=10r/s 是单 IP 平均每秒 10 个请求;burst=20 nodelay 允许瞬时突发 20 个、超出的立即拒绝不排队;limit_conn 10 限制单 IP 同时最多 10 个并发连接;limit_req_status 429 让被限请求返回 429 而非默认 503,方便客户端识别限流。阈值按接口正常峰值压测后设定,宁松勿紧。
Q:只按 IP 限频为什么不够?key 该怎么选?
因为攻击者用代理池和秒拨 IP,换 IP 的成本几乎为零。正确做法是分层限频:IP 层粗限制挡散兵游勇,业务 key 层精细限制挡定向攻击。有登录态的接口,用 token 或用户 ID 做 key 更准:
map $http_authorization $api_key {
"" $binary_remote_addr;
default $http_authorization;
}
limit_req_zone $api_key zone=api_token:10m rate=30r/s;
server {
location /api/ {
limit_req zone=api_token burst=50 nodelay;
...;
}
}
这样即使攻击者换几百个 IP,只要共用少量 token,依然会被限住。短信、登录这类高危接口,再用手机号、目标账号做第三层 key。
Q:签名校验和防重放怎么做?
限频是”限多少”,签名解决的是”能不能随便调”。标准做法:
- 客户端对 请求参数 + 时间戳 + 随机 nonce 做 HMAC-SHA256 签名,密钥不下发在前端明文可见的位置。
- 服务端校验签名一致,且时间戳在 ±300 秒 窗口内,超窗拒绝。
- nonce 写入 Redis 并设置与时间窗口等长的 TTL,重复出现即判定为重放攻击,直接拒绝。
# 服务端校验伪代码(Python)
sign = hmac.new(secret, params + ts + nonce, hashlib.sha256).hexdigest()
if sign != client_sign: reject(401)
if abs(now - ts) > 300: reject(403) # 时间窗口防重放
if redis.exists("nonce:" + nonce): reject(403)
redis.setex("nonce:" + nonce, 600, 1)
三道检查缺一不可,尤其是 nonce 防重放——很多接口只校验时间戳,攻击者把同一个请求在 5 分钟窗口内打几十万次照样有效。
Q:验证码和人机校验该放在哪一层?
不是所有接口都要弹验证码,放错层会伤正常用户:
- 登录、注册、找回密码、短信发送:前置图形验证码或行为验证码,这类接口是撞库和轰炸的重灾区。
- 全站请求入口:JS 挑战、Cookie 校验这类人机识别,交给 WAF 或 CDN 在边缘做,不占用源站资源。
- 业务 API 本身:以限频 + 签名为主,验证码只作为触发式手段——某个 key 触发限频后,要求带验证码 token 重试。
Q:高防和 WAF 在 API 防 CC 里起什么作用?
Nginx 限频和签名都在源站执行,攻击流量已经到了你的服务器;CC 流量一大,源站带宽和连接数可能先被占满。WAF/高防的价值是把防线前移:在云端边缘完成频率分析、UA 指纹识别、JS 挑战和人机校验,只把正常请求回源。像百度云防护这类方案对 CC 有专门的频率指纹策略,配合 CDN 边缘节点吸收攻击流量,源站在攻击期间基本无感。自建限频是基础功,前面挂一层 WAF 是省心解,两者不冲突。
Q:被 CC 后直接封 IP 段行不行?
能救急,但别当长期手段:
- 封 C 段容易误伤运营商 NAT 出口,一个出口后面是成百上千正常用户。
- 代理池换段成本极低,封一个段攻击者马上换下一个。
- 封禁要自动化 + 短 TTL:用 fail2ban 之类工具按日志自动封、自动解,而不是手工 iptables 永久封禁。
顺序建议:先限频(429 熔断)→ 触发限频次数多的 IP 再自动临时封 → 确认恶意特征明显的段才考虑长封。
Q:还有什么补充建议?
API 防 CC 是个体系活,最后给四条建议:一是建立 QPS 基线告警,攻击发生 5 分钟内就知道,比事后看监控强得多;二是准备降级预案,非核心接口被打时直接返回静态 503,保住核心交易链路;三是限频规则上线前必须压测,自己先把自己的接口打死一次才知道阈值在哪;四是如果团队没有精力维护这套体系,直接上带 CC 防护能力的云端 WAF,把边缘限频、人机校验、攻击报表交给专业方案,把精力留给业务本身。