CC攻击多站联防怎么做?黑名单共享与统一封禁

同一个攻击源被封在 A 站,换到 B 站照样放行,这是多站运营最常见的防守漏洞。CC攻击多站联防要解决的,就是把分散在各站的封禁数据集中起来、统一生效。本文用 10 个问答讲清共享黑名单的几种做法、配置怎么写、怎么防投毒、误封了怎么回滚。

Q:CC攻击多站联防到底解决什么问题?

  • 单站防守最大的浪费,是每一次攻击都要从头重新识别一遍。攻击者用同一批 IP 轮流打你的几个站点,A 站刚封完,B 站还要重新观察、重新统计、重新确认。
  • 三类具体损失:
  • 封禁不共享:同一批源 IP 在多个站点重复消耗人力与防护额度。
  • 判断不共享:A 站已经确认的特征(同一 JA3 指纹、同一 UA、同一路径规律),B 站在被打时完全不知道。
  • 多站联防的价值是把「一次确认」变成「全站生效」,把攻击者的成本从「换一个站」抬高到「换一批资源」。
  • 但要注意:它同步的应该是高置信度的结论,而不是所有封禁。范围失控,误封也会被放大。

Q:共享黑名单有哪几种实现方式,怎么选?

  • 按复杂度从低到高排,逐级升级即可,不必一步到位:
  • 共享文件加定时同步:各站把封禁列表写成文本,用 rsync 或对象存储同步,定时校验后 reload。成本最低,分钟级生效。
  • Redis 集中存储:所有站点写同一份封禁集合,借助 TTL 自动过期。秒级生效,天然支持分级与自动清理,多站场景性价比最高。
  • 边缘层统一生效:如果各站都接在同一套 CDN 或 WAF 后面,直接用服务商的自定义 IP 组下发,源站不用改配置。
  • 接口推送:自建一个小服务,各站通过带鉴权的接口查询或订阅封禁名单。适合站点多、技术栈不统一的团队。
  • 选择标准:站点少且同机,用共享文件;多机房多机器,用 Redis;接在同一层防护后面,优先用防护平台的自定义 IP 组。

Q:用 Redis 做共享黑名单,落地配置长什么样?

  • 思路是Redis 负责存与过期,Nginx 负责判断与拒绝。Redis 只提供一份「当前有效的封禁 IP 清单」。
  • 第一步,写入与自动过期。分级用不同的集合,过期时间由 TTL 控制:
# 观察级:只统计不限速,保留 1 小时
redis-cli SADD waf:cc:watch 203.0.113.9 EX 3600

# 限速级:进入限流白名单,保留 24 小时
redis-cli SETEX waf:cc:limit:203.0.113.9 86400 1

# 封禁级:直接拒绝,保留 7 天
redis-cli SETEX waf:cc:block:203.0.113.9 604800 1

# 查看当前封禁总数
redis-cli --scan --pattern 'waf:cc:block:*' | wc -l
  • 第二步,把 Redis 里的封禁 IP 导出成 Nginx 能读的文件。用原子替换,避免 reload 瞬间读到半截文件:
#!/bin/bash
# /usr/local/bin/cc-sync.sh —— 每 30 秒执行一次
set -e
TMP=/etc/nginx/conf.d/cc-block.conf.new
redis-cli --scan --pattern 'waf:cc:block:*' \
  | sed 's|.*waf:cc:block:||' \
  | awk '{print "deny " $1 ";  # cc-shared"}' > "$TMP"

# 内容一致就不动,减少无意义的 reload
cmp -s "$TMP" /etc/nginx/conf.d/cc-block.conf && rm -f "$TMP" && exit 0

mv "$TMP" /etc/nginx/conf.d/cc-block.conf
if nginx -t 2>/dev/null; then
    nginx -s reload
else
    echo "config invalid, rollback" 1>&2
    git -C /etc/nginx checkout -- conf.d/cc-block.conf 2>/dev/null || true
    exit 1
fi
  • 第三步,主配置里放一行 include,让黑名单独立成文件。回滚就是注释掉这一行:
# /etc/nginx/nginx.conf(http 段内)
include /etc/nginx/conf.d/cc-block.conf;

# 定时任务
* * * * * /usr/local/bin/cc-sync.sh
* * * * * sleep 30; /usr/local/bin/cc-sync.sh
  • 注意:Nginx 的 deny 列表不支持热加载,每次更新都要 reload。同步脚本里加 cmp 判断内容是否真的变了,可以把 reload 频率压到最低。

Q:不想频繁 reload,还有别的方案吗?

  • 有,思路是把封禁落到限流键上,用一个阈值极低的独立 zone 等效于拒绝,配置改一次即可长期生效:
# 黑名单 IP 命中 1,正常 IP 命中 0
geo $cc_block {
    default 0;
    include /etc/nginx/conf.d/cc-block-geo.conf;   # 内容形如 203.0.113.9 1;
}

# 黑名单专用 zone:阈值给到极低,等效于拒绝
limit_req_zone $cc_block zone=cc_drop:10m rate=1r/m;

server {
    limit_req zone=cc_drop burst=1 nodelay;
    limit_req_status 429;
}
  • 如果站点已经接在 CDN 或 WAF 后面,最省事的做法是用防护平台的自定义 IP 组:改一次全部站点生效,源站零配置。

Q:多站共享黑名单,怎么避免把误封放大?

  • 这道防线必须在上线前设计好,否则一次误判会波及全部站点。三条规则:
  • 分级,只有最高级才共享。观察级留在本站,限速级只在同集群共享,只有经过多站多次命中或人工确认的封禁级才同步到全部站点。
  • 封禁时长分级。短封禁(1 小时)优先,续封(24 小时)看复犯次数,长封禁(7 天以上)只给特征非常明确的源(例如固定 UA 加固定路径的脚本)。
  • 一切都要有到期时间。黑名单只会增长不会收缩,是最典型的技术债。用 TTL 让封禁自动消失。
# 用 zset 存封禁,score 设为到期时间戳,清理时按分数范围删除
redis-cli ZADD waf:cc:block:z 1750000000 203.0.113.9
redis-cli ZREMRANGEBYSCORE waf:cc:block:z -inf $(date +%s)
redis-cli ZCARD waf:cc:block:z
  • 再配一条保险:任何共享黑名单在执行拒绝前,先过一遍白名单。白名单永远优先,且各站本地可覆盖全局规则。

Q:哪些来源绝对不能共享封禁?

  • 这是多站联防里最容易出事故的地方。以下类型即使命中了规则也要排除:
  • 运营商大 NAT 出口、公共 DNS 与云厂商探针:一个 IP 背后可能是几万个真实用户。
  • 搜索引擎蜘蛛的官方 IP 段:误封的代价是掉收录,而且恢复很慢。
  • 企业客户的固定出口 IP,以及自有办公网、CI 构建机、压测出口。
  • 防护节点与 CDN 回源段:把上游封了,等于自己切断回源链路。
  • 落地方式:在写入侧过滤一次,把白名单与保留网段挡在入库之前——比在十个站点上各漏一次更可靠。
# 入库前过滤:保留网段与白名单不进共享层
ipset add cc_whitelist 10.0.0.0/8
ipset add cc_whitelist 203.0.113.0/24   # 替换为自家网段
ipset test cc_whitelist 203.0.113.9 && echo skip || redis-cli ZADD waf:cc:block:z $(($(date +%s)+86400)) 203.0.113.9

Q:多站之间交换黑名单,怎么防止被人投毒?

  • 共享黑名单本质上是一个可信数据源,一旦被投毒,攻击者可以让你的所有站点同时拒绝某个正常客户,或者把自己的攻击 IP 洗白。四道防护:
  • 鉴权与来源白名单:接口只接受带共享密钥的请求,并对调用方做 IP 白名单,密钥定期轮换。
  • 字段最小化:只传 IP、命中时间、规则编号与置信度,不传日志原文、不传 URL 参数,避免共享同时泄露业务数据。
  • 单向订阅:低置信度的站点只能订阅,不能写入共享层。写权限只给确认过的节点。
  • 来源可信度评分:给每个来源站一个权重,只有权重达标的结论才进入封禁级;同时保留「谁写入的」字段,出问题能立刻撤销整批。
  • 如果只是同步自家的多个站点,更简单的做法是不开放任何接口。

Q:除了共享黑名单,多站联防还能做什么?

  • 黑名单是最基础的一层,往上还有三件事值得做:
  • 统一日志汇聚:把各站访问日志集中到一处,用同一个脚本统计来源 TOP、路径 TOP、UA 种类数,攻击换目标时不用重新搭环境。
  • 特征级共享:比 IP 更稳的是特征——同一 JA3 指纹、同一组 UA、同一套请求路径规律。IP 池可以秒换,行为特征换起来慢得多。
# 各站日志按小时汇聚后统计,跨站来源 TOP 一目了然
cat /var/log/nginx/*.access.log | grep -E ' 429 | 403 ' \
  | awk '{print $1, $7}' | sort | uniq -c | sort -rn | head -30
  • 提醒一句:IP 黑名单的时效性很差,秒拨与代理池换得很快。共享层存 IP 是必要的,但真正决定防护效果的是「特征 + 阈值 + 行为」,IP 只是最后一公里的执行手段。

Q:还有什么补充建议?

  • 顺序上建议先把单站闭环做好,再做跨站共享。直接上联防只会把没理顺的问题同步复制到所有站点。
  • 白名单永远优先,各站要能本地覆盖全局结论,别让一次错误的共享封禁把自己人挡在门外。
  • 把共享层当成有 SLO 的组件来运维:它挂了不应该导致全站不可用。设计上要允许读取失败时降级为本站黑名单,而不是直接放行或直接全拒。