华为云WAF误拦截定位与精准放行
华为云 WAF 拦掉了正常用户:一次误拦截的定位与精准放行
用户投诉"文章保存不了",但后端日志里一条请求都没有。查了两小时应用代码无果,最后发现请求根本没到应用——它在 WAF 就被拦了。更麻烦的是,触发拦截的是用户文章里的一段代码示例。安全设备的每一次误判,在业务侧都表现为一次随机的功能故障。
一、现象:只有一部分人报错
一次典型的误拦截长这样:
| 观察项 | 现象 |
|---|---|
| 用户反馈 | “保存文章失败”,部分用户,非全部 |
| 复现 | 自己测试完全正常 |
| 后端应用日志 | 查不到失败请求 |
| 浏览器表现 | 提交后无响应,或跳到一个空白错误页 |
| 受影响功能 | 集中在富文本、代码分享、评论 |
"自己复现不了 + 后端无日志"是这类问题的典型组合。后端没日志,说明请求没到后端,嫌疑范围立刻缩小到:DNS、CDN、WAF、负载均衡。
二、第一步:确认拦截发生在哪一层
按请求链路逐层排查:
浏览器 → DNS → CDN/WAF → ELB → 应用
↑
大概率卡在这里
三个快速判断:
- 抓响应体:WAF 拦截通常返回 403,且响应体是拦截提示页(可能带请求 ID),与应用自己返回的 403 页面明显不同
- 对比响应头:WAF 拦截的响应往往缺少应用自定义的 header
- 看 WAF 控制台的拦截日志:用时间点 + URL + 源 IP 过滤,能直接看到命中的规则名
本案在第 3 步就确认了:日志里明确显示该请求命中了 SQL 注入规则。
三、为什么会被误判:规则引擎不理解语义
这是理解所有 WAF 误报的前提:
WAF 做的是模式匹配,不是语义理解。它判断的是"这段字符串长得像不像攻击",而不是"这段字符串想干什么"。
用户提交的文章内容是这样的(技术博客,正文里贴了一段 SQL 示例):
SELECT id, name FROM users WHERE id = 1 UNION SELECT 1,2,3 --
对 WAF 来说,这段 payload 同时命中了:union select、-- 注释符、from 关键字——规则判定为典型的 SQL 注入攻击特征,拦。
对用户来说,他只是想分享一段代码。
四类高频误伤场景
| 场景 | 触发原因 | 典型业务 |
|---|---|---|
| SQL 注入规则 | 正文含 union select、'、--、/* */ |
技术社区、代码分享、工单系统 |
| XSS 规则 | 正文含 <p>、<img onerror>、javascript:、onclick |
富文本编辑、邮件模板、CMS |
| CC 防护 | 单 IP 请求频率超阈值 | 企业 NAT 出口、校园网、移动基站 |
| 爬虫防护 | UA 含 python、curl、spider 等关键词 |
监控探针、开放平台 SDK |
其中 CC 防护的误伤最容易被忽略也最致命:整栋写字楼几百人共享一个出口 IP,一个人刷新快一点,全楼被限流。表现为"公司内网访问不了,手机热点正常"——这个现象非常有辨识度。
四、定位:五步缩小到具体触发点
1. 拿到原始请求
从 WAF 攻击日志里导出该请求的完整 payload(URL、header、body)。这是后续所有分析的基础,不要用用户复述的内容,一定要原始报文。
2. 看命中的规则名
日志里通常有 rule_id 或规则组名称。记住它,后面处置要用。
3. 二分法定位触发字段
如果 body 有多个字段,逐个删除重放,直到不触发为止:
原始 body(触发)
→ 删除 field A(仍触发)
→ 删除 field B(不触发了) ← 定位到 B
→ 把 B 的内容再二分
→ 找到具体的触发字符串
4. 确认编码问题
同一段内容,URL 编码前后可能表现不同:
%3Cscript%3E解码后是<script>,WAF 通常会解码检测- 双重编码
%253Cscript%253E可能绕过检测(这是攻击手法),也可能导致误判
不要指望"编码就绕过"——那是绕过防护,等于主动关闭安全能力,属于严重的安全反模式。
5. 复现验证
在测试环境用相同 payload 提交一次,确认能稳定复现拦截。不能稳定复现的不要急着改规则,否则可能掩盖真正的问题。
五、处置:三级方案,从最精准到最粗暴
很多人一遇到误拦截就去关规则组,这是最差的做法。正确的处置有三档:
第一档:精准放行(首选)
只对"特定接口 + 特定参数"关闭特定检测,其他一律不变:
放行条件:URL = /api/article/save AND 参数名 = content AND 规则 = SQL 注入
范围越小越好。不要写成 URL 前缀 = /api/ 就放行全部——那等于给整个 API 开了后门。
第二档:规则例外
对某条规则设置例外条件,而不是关闭整组。比如"XSS 规则对 /api/article/save 的 content 字段不生效",但 SQL 注入检测仍然保留。
这样即使攻击者往 content 里塞 SQL 注入,依然会被拦。
第三档:关闭规则组(尽量避免)
关闭整组规则会让所有接口失去这一层防护。只有在业务完全不可用、且其他方案都来不及的时候才用,并且必须记录、设定恢复期限。
一个必须纠正的误区:按 IP 加白
“把这个客户的 IP 加白名单”——听起来简单,实际很危险:
- IP 可能是 NAT 出口,加白等于放行整个网段的所有人
- IP 会变,加白名单会越加越多,最后变成一张没人维护的漏洞清单
- 攻击者只要拿到一个白名单 IP(比如通过代理),就完全绕过防护
优先按"接口 + 参数"放行,而不是按"来源 IP"放行。
六、富文本场景的通用解法
富文本、代码分享这类业务,天生和 XSS 规则冲突。成熟的解法是分层:
| 层 | 做法 |
|---|---|
| 输入层 | 限制字段长度、限制允许的 HTML 标签集合 |
| 应用层(关键) | 服务端做 HTML 白名单过滤,如 OWASP Java HTML Sanitizer |
| WAF 层 | 该接口关闭 XSS 检测,保留 SQL 注入、命令注入等检测 |
| 输出层 | 渲染时做转义,配合 CSP |
核心思路:把 XSS 防护的责任交给应用层的白名单过滤,WAF 只负责它擅长的模式匹配类攻击。
应用层过滤的可靠性远高于 WAF,因为它理解 DOM 结构;而 WAF 只能看字符串。用 WAF 来防富文本的 XSS,本质上是用错工具。
七、CC 防护:别用 IP 计数硬扛
CC 防护的阈值是按"单 IP 请求频率"算的。在 NAT 场景下这个模型天然失真。
更合理的组合:
| 维度 | 说明 |
|---|---|
| 行为特征 | 是否有正常会话、是否携带合法 Token |
| 人机校验 | 超过阈值先弹验证码,而不是直接封 |
| 分接口阈值 | 登录接口严格、静态资源宽松,不要全局一个值 |
| 观察期 | 阈值调整后观察一周真实攻击拦截量 |
推荐"先验证码、再限流、最后封禁"的渐进策略,而不是一超阈值就 403。这样正常用户最多多输一次验证码,不至于整栋楼断网。
八、上线前必须做的验证
改完规则不能直接全量生效,要做灰度:
| 步骤 | 动作 | 观察指标 |
|---|---|---|
| 1 | 灰度 10% 流量 | 误拦截率是否下降 |
| 2 | 观察 24 小时 | 真实攻击拦截量是否下降(关键) |
| 3 | 逐步放量到 100% | 403 比例、业务成功率 |
| 4 | 建立误报台账 | 记录规则 ID、触发场景、处置方式 |
第 2 步最容易被跳过,但它是唯一能证明"你没有为了体验牺牲安全"的证据。误拦截率降了、攻击拦截量也断崖式下降,说明你放得太宽了。
九、小结
四句话:
- 后端无日志 + 部分用户报错 = 请求没到后端,先查 WAF/CDN 这一层,别在应用代码里耗时间。
- WAF 做的是模式匹配,不理解语义,技术内容、代码示例、富文本天生容易误伤。
- 放行要精准到"接口 + 参数 + 规则"三个维度,别按 IP 白名单——NAT 场景下那是给自己挖坑。
- 改完规则必须同时看误拦截率和真实攻击拦截量,只看前者等于在给安全能力做减法。
如果只记一句:误拦截的正确解法是缩小检测范围,而不是关掉检测。
- 点赞
- 收藏
- 关注作者
评论(0)