DDoS防护怎么验收?别只看供应商报表

高防买了、CDN 也接了,攻击来的时候到底防住没有?这个问题没法靠供应商报表回答。本文用 10 个问答讲清 DDoS防护的验收方法:压测怎么设计、源站日志怎么看才叫干净、误杀率怎么量化、业务指标怎么对比,以及报表里哪些数字不能信。

Q:为什么不能只看供应商的清洗报表?

  • 报表是平台视角,只能证明「到我这儿的流量我拦了多少」,证明不了「你的源站有没有被打到」。这两个结论之间隔着一条回源链路。
  • 三个常见口径陷阱:清洗峰值按入向流量算、可用性用自家节点探测、攻击次数把扫描探测也算进去。数字都很漂亮,但和你的业务感受常常对不上。
  • 验收的锚点只有一个:源站日志干净,业务指标不退化。平台数据是佐证,不是结论。
  • 做法是三方对齐时间轴:平台清洗量、源站日志、业务监控,三条线在同一个时间点上的表现必须能互相解释。解释不通的那一段,就是没验到的地方。

Q:验收要覆盖哪几个维度?

  • 可用性:外部多点拨测,1 分钟一次,攻击时段可用率不低于 99.9%。
  • 源站承压:源站 5xx 占比低于 1%,QPS 峰值不超过日常峰值的 2 倍。
  • 误杀:403 与 429 占全部请求的比例低于 0.5%,且支付、登录、搜索引擎蜘蛛零误拦。
  • 业务指标:下单成功率、登录成功率、核心接口 P95 延迟,不低于攻击前基线的 95%。
  • 前两项证明防住了,后两项证明没防过头。只验前两项,很容易把「拦得太狠」当成「防得很好」。

Q:压测怎么做,既验得出效果又不惹事?

  • 三条铁律:只打自己的站、提前向机房与高防供应商报备、随时能停。
  • 压力要阶梯加,不要一上来打满。从日常峰值的 20% 起,每 5 分钟加一档,每档记录源站 CPU、连接数、5xx。
  • 七层用 wrk 打,四层用 hping3 打 SYN。后者需要 root,且部分上游会直接丢弃,属正常现象。
# 七层阶梯压测(并发逐级上调,每档 30 秒)
for c in 50 100 200 400 800; do
  echo "=== concurrency $c ==="
  wrk -t4 -c$c -d30s --latency https://www.example.com/ | tail -5
  sleep 5
done

# 四层 SYN 试探(小规模,观察是否触发清洗)
hping3 -S -p 443 --flood --rand-source -c 20000 203.0.113.10
  • 每档结束立刻看一眼源站:CPU 是否线性上涨、ss -s 的 TIME_WAIT 是否暴增。任一指标越线就停,别为了凑数据把源站打挂。
  • 不要拿压测流量去测供应商,那是违规的,而且测出来的也不是你的真实链路。

Q:怎么从源站日志证明「没被打穿」?

  • 按分钟切分,看 5xx 比例和 QPS 两条曲线,与攻击前的基线对比,而不是跟昨天比。
# 按分钟统计 QPS 与 5xx 占比
awk '{ split($4, t, ":"); m = t[2]":"t[3];
       n[m]++; if ($9 ~ /^5/) e[m]++ }
     END{ for (k in n) printf "%s  req=%d  5xx=%.2f%%\n", k, n[k], e[k]*100/n[k] }' \
  /var/log/nginx/access.log | sort | head -30
  • 再分一层看错误来源:upstream_status 有 5xx 说明源站自己扛不住;499 多说明是客户端先断开;两者都不该在攻击时段大量出现。
  • 关键判据:攻击源 IP 在源站日志里应该几乎不出现。如果它们大面积出现在源站日志中,说明防护在放水——流量被放到了后端才开始处理。
  • 日志格式里务必带上 $request_time 和 $upstream_response_time,否则「慢」这件事无法归因到哪一层。

Q:误杀率怎么量化,而不是拍脑袋?

  • 两个办法:样本回放(把攻击前 24 小时的真实请求重放到当前规则上)和 灰度对比(一部分流量走新规则,对比两组 403/429 差异)。
  • 回放最直接:正常用户的请求被新规则拦掉的比例,就是误杀率。用日志导出的 UA + 路径 + 频率做输入即可。
  • 看分布比看总数有用:如果 429 集中在某一类 UA、某个地域、某个接口,说明规则切得太粗。
# 找出被限流最多的来源与路径
grep ' 429 ' /var/log/nginx/access.log \
  | awk '{print $1}' | sort | uniq -c | sort -rn | head -20
grep ' 429 ' /var/log/nginx/access.log \
  | awk '{print $7}' | sort | uniq -c | sort -rn | head -20
  • 三类请求必须零误杀:支付回调、登录、搜索引擎蜘蛛。这三类被拦,损失比被打还大。

Q:业务指标怎么对比才有说服力?

  • 取攻击前 7 天同时段做基线,不要用全天平均,也不要用上个月的数据。
  • 四个指标够用:下单成功率、登录成功率、核心 API 的 P95、页面跳出率。前三个从业务埋点拿,最后一个从统计工具拿。
  • 对比结论要写成差值,不写形容词。例如「下单成功率 63.2% → 61.8%,下降 1.4 个百分点,在正常波动内」,比「基本正常」有说服力得多。
  • 如果业务指标掉了但可用性 100%,多半是限流误杀了关键接口,回到上一条去查 429 分布。

Q:高防的「清洗能力」怎么验?

  • 先分清两个单位:Gbps 是带宽,pps 是包速率。64 字节小包下 1Gbps 约等于 118 万 pps,标称 300Gbps 不等于扛得住 1000 万 pps 的 SYN 洪水。
  • 采购或验收时要问三个数:保底防护、弹性峰值、峰值能持续多久。只报一个数字的,基本是按最好情况写的。
  • 试探方法:小规模 SYN 洪水(几千 pps 起),观察多久触发清洗、触发后正常请求延迟是否抖动。触发慢说明阈值设高了,一碰就触发说明设低了。
  • 阈值这件事没有标准答案:太低会常态误杀,太高等于没防护。合理区间通常是日常峰值的 1.5 到 3 倍,按业务波动性调。

Q:CDN 与高防的回源链路怎么验证?

  • 最容易被忽略的一环:源站必须只允许回源网段访问。否则攻击者绕过 CDN 直连源站,前面所有防护归零。
# 直连源站并伪造 Host,正常应被拒绝或超时
curl -sS -o /dev/null -w '%{http_code}\n' \
  -H 'Host: www.example.com' http://203.0.113.10/

# 确认请求确实经过了 CDN
curl -sSI https://www.example.com/ | grep -iE 'x-cache|age|via|server'
  • set_real_ip_from 必须写具体的回源网段,不能写 0.0.0.0/0。写错了,日志里的真实 IP 可被伪造,后面所有基于 IP 的限流全部失效。
  • 用 X-Forwarded-For 做限流键之前,先确认取的是第几段。取错位置等于给攻击者留了一个换键的口子。

Q:验收报告要写什么?

  • 五节结构:范围与时间、方法与工具、原始数据、逐项结论、遗留项。一页结论加附件数据,别写成散文。
  • 结论要写「哪项没达标」,不要写「整体良好」。验收的价值在于把问题暴露在纸面上。
  • 遗留项必须带责任人和时间点:哪条规则要调、哪个阈值要重设、哪台源站要扩容。
  • 报告存档。下次攻击、下次续费、下次换供应商,这份基线就是最有力的谈判材料。

Q:还有什么补充建议?

  • 验收不是一次性动作:季度复验一次,每次防护规则大改后必须复验。规则会漂移,基线也会过期。
  • 平时就要留数据:每天固定时间导一份 QPS、5xx、P95 的基线,出事才有对比物。临时找基线,永远找不到。
  • 把「压测与验收」写进合同的服务交付项,别只买清洗能力不买验证义务。
  • 最后一句:供应商的报表说明它尽力了,源站日志才说明你安全了。验收就是把这句常识变成流程。