接口回归大面积假绿的根因:断言停在"我发了什么",没人核对应用真正给了什么。
你打开内部工单系统,没登录,地址栏里把路径改成 /admin。屏幕上出来的是用户管理列表、重置密码按钮。你切回流水线那一页:接口回归是绿的,安全用例那一栏也是绿的。两栏都没说谎,它们只是没被要求回答"后台到底露没露"。
绿灯是怎么漏掉事故的
断言的靶子错了。我们写接口用例时,断言的对象长期是两样东西:我发出去的请求长得对不对(路径、参数、payload 里有没有那个坏字符串),以及安全设备有没有把它当坏请求处理(有没有命中规则、有没有被入口拒掉)。这两样都在请求侧。而"应用拿到这个请求之后做了什么、最后回了什么",在绝大多数用例里没有对应的断言行。
于是三类事故共用同一条漏网路径。第一类,前置鉴权组件配置漂移:调试时有人把网关的鉴权开关关掉过,没改回来,接口照样通,用例照样绿,因为用例本来就在等一个 200。第二类,安全设备的规则只匹配请求特征:请求里那个穿越串今天被规则认识了,明天源站换了路由,穿越串被源站当成普通路径忽略掉,返回一个像正常页面又不太一样的响应——请求侧看不出任何异常。第三类,"被拦了"被当成"安全":入口拦下一次,用例记一次通过,可这条用例从来没测过"没拦住的那次会发生什么"。
官方把哪两个问题写进了产品
据 Cloudflare 官方博客,这次给 WAF 加了两项能力:full transaction detections(把一条 HTTP 请求与它的响应放在一起评估,用来识别"利用成功"的证据)与 response rules(在源站响应到达请求方之前把它拦下来)。官方把要回答的问题摆成两句:这条请求看起来坏吗?应用处理它之后发生了什么?前一句是老本事,后一句才是新加的那格。
同一篇里还有一句话可以直接抄进评审会:响应提供了缺失的证据(The response provides the missing evidence.)。理由是只看请求无法确定应用到底有没有把文件暴露出去——它可能拒绝,可能把穿越串忽略掉,也可能返回一个错误页。三种情况在请求侧长得一模一样。
官方给了一条示例规则,形状值得测试工程师照抄:
http.request.uri.path starts_with "/admin"
and not http.request.headers["x-internal-user"][0] eq "true"
and http.response.code eq 200
and http.response.headers["content-type"][0] contains "text/html"
官方的解释同样关键:只有当未鉴权请求打 /admin、且源站确实返回了 200 的 HTML 管理页时才拦;普通的鉴权失败、重定向、返回的不是管理页,都不拦。一条规则里同时压着请求侧条件和响应侧结果,缺一个都不判。

图注:同一批探针在三种断言靶子下的判红判绿——只有第三种在"网关配错"那一行变红,其余三行都不误报。
结果断言要同时对上四件事
结果断言(outcome-based assertion,断言对象是应用处理之后的真实结果,而不是输入的可疑程度)落到代码上是四件事:请求侧的前置条件不成立(未鉴权、来源不合法)、状态码是成功、内容类型是这个资源该有的类型、响应体命中业务特征。前一件管"这次访问本来不该成",后三件管"它真的成了"。
只写后三件会退化成一条可用性用例;只写前一件就还是那个停在请求侧的老断言。
这里有个容易做过头的地方要说清楚:结果断言不是"把整个响应体都比一遍"。响应侧要断言的是"这件事真的成了没有",粒度落在几个稳定的业务特征上——一个状态、一个内容类型、两三处只在这个资源里出现的字样。全文比对会在源站换文案的第一天就误杀,而那种误杀最终会让整条断言被关掉,回到只断言 200 的原点。
一个能跑的 pytest 夹具
业务场景就用开头那件事:内网后台前面套一层鉴权网关,网关配置漂移时 /admin 直接暴露。三种断言靶子写在同一份代码里,用同一批探针跑,看谁能发现、谁会被误伤。夹具只用标准库,python 01_full_transaction_assertion.py 直接出对照表,pytest 收进去就是三条用例。
# 01_full_transaction_assertion.py(节选,完整文件含网关、源站与三条用例)
ADMIN_MARKERS = ("用户管理", "重置密码", "批量删除")
def assert_request_side(reply):
"""靶子一:只断言请求侧——入口有没有把它当坏请求拦下来。"""
return not reply.blocked_at_entry
def assert_status_only(reply):
"""靶子二:只断言状态码——有响应且是 200 就当链路正常。"""
return reply.status == 200
def assert_full_transaction(path, headers, reply):
"""靶子三:请求侧条件不成立 + 响应侧真实结果同时成立,才判暴露。"""
reasons = []
unauthed = path.startswith("/admin") and headers.get("x-internal-user") != "true"
real_admin_page = (
reply.status == 200
and "text/html" in reply.content_type
and sum(m in reply.body for m in ADMIN_MARKERS) >= 2)
if unauthed and real_admin_page:
reasons.append("未鉴权请求拿到 200 的 HTML 管理页:状态码=%d 类型=%s 命中特征=%s"
% (reply.status, reply.content_type,
[m for m in ADMIN_MARKERS if m in reply.body]))
return (not reasons), reasons
# 实跑(本机 2026-10-28):五条探针里只有"网关配错"那行变红,其余四行不误报;3 条用例全过
跑出来的对照(本机 2026-10-28 实际输出摘要):未鉴权打后台且网关配错——只断请求=通过、只断状态码=通过、全交易=红,红因点名"状态码=200 类型=text/html 命中特征=['用户管理','重置密码','批量删除']";网关正常、鉴权后访问、穿越被源站忽略、普通接口请求四行全交易均为绿,无误报。三条 pytest 用例全过。
为什么这么写、这么断会漏什么,四条。第一,管理页特征取的是"命中两个以上"而不是全文匹配:源站的模板里那些字样可能只出现在导航,也可能整页改版后一个都不剩,单条特征既会漏也会误伤,计数是把判定摊到多个弱信号上。第二,unauthed 这个请求侧条件不能省:省掉它,这条断言就变成"任何返回 HTML 管理页的响应都有罪",鉴权后的正常访问第一条就被误杀。第三,返回 (ok, reasons) 而不是布尔值:门禁要能在 CI 日志里点名是哪一条合取成立,只给一个 False,第二天还是会有人在评审会上问"那到底哪儿不对"。第四,穿越被源站忽略这一行专门留着当误报控制——应用没把文件给出去,断言就不该红,这是响应侧断言最容易被人做过头的地方。
三种靶子各能看见什么
| 断言靶子 | 能发现什么 | 漏什么 | 误报代价 |
|---|---|---|---|
| 只断言请求侧(入口拦没拦、payload 像不像坏的) | 规则生效、已知攻击串被拒 | 前置鉴权配错、源站自己放行、绕过规则的新串 | 极低,几乎不误报 |
| 只断言状态码 | 服务挂了、路由 404 | 未鉴权拿到 200 的真实业务页面,全都绿 | 低,但会把正常重定向判错 |
| 全交易断言(请求条件不成立+响应真实结果) | 利用成功这件事本身:配置漂移、规则没覆盖的绕过、响应里多出来的东西 | 源站内部已经发生但没写进响应的动作 | 取决于特征字段,需要一轮观察期校准 |

挂进回归链的三个动作
其一,把这类断言从安全用例挪进业务用例的收尾。核心链路跑完之后,用一条未鉴权探针打一遍敏感路径,判红判绿走同一个断言函数——安全用例单独放的时候,没人要求它跟着业务改版一起跑。其二,给"响应侧特征"设一个只记录不判红的观察期,跑一到两周,把命中集拉出来给业务方看过一遍再开门禁,误杀会集中在改版期的模板文案上。其三,敏感路径清单要有 owner 并进版本库:/admin 之外还有哪些路径属于"未鉴权拿到 200 就该红",这份清单比规则本身更容易腐烂。
边界:这条断言管不到什么
第一,响应侧断言拿不到源站内部已经发生的动作——它只能看见被写进响应里的那部分事实,这一点本篇不展开。第二,合取条件越多误报越准也越脆:官方那条规则依赖具体的 header 名与路径前缀,你的网关换了 header,规则就静默失效,所以断言本身也要有回归用例。第三,能判"利用成功"不代表能判"没有利用":应用返回 404 只说明这一次没成。第四,这套断言是门禁不是防御,它抓的是已经暴露的事实,不替代前置的鉴权与网络隔离。
下一步
挑一条你们最信任的接口用例,把它那句 assert 念一遍,问一句"如果网关配错了,这行会变红吗"。不会变红的断言,测的是你们发了什么,不是应用给了什么。
断言停在请求上,绿一百次也只是流程走完;断言搬到结果上,第一次红就是救回来一次事故。
下次评审接口用例,把那张三行表投到屏幕上,逐条问一句:这条断言看的是请求,还是响应。
本文部分内容参考了霍格沃兹测试开发学社整理的相关技术资料,主要涉及软件测试、自动化测试、测试开发及 AI 测试等内容,侧重测试实践、工具应用与工程经验整理。