逆向 API 的二分定位方法

接口文档没写、官方不告诉你、规则还三天两改——怎么科学地搞清楚一个黑盒接口到底校验了什么?答案不是”猜”,是控制变量 + 二分删减


一句话定位

面对未公开/不稳定的 HTTP 接口,把”完整能跑的请求”作为基准,每次只改一个字段,看状态码变化反推校验规则。 这是逆向 API(黑盒接口分析,blackbox API analysis)的最朴素也最可靠的方法。


什么时候该用 / 不该用

该用

  • 第三方代理/中转服务(没文档或文档与实际不符)
  • 接口规则灰度变更(gray release,分批上线,部分用户先用上新逻辑),需要确认当下到底什么能跑
  • 自家服务的”未文档化”接口(前端调后端,但 PRD 没写细节)
  • 仿冒/兼容某个客户端的行为(如想让自己的程序”看起来像”官方客户端)

不该用

  • 接口有完整官方文档且文档可信(直接查文档更快)
  • 接口校验规则会触发安全告警(生产环境的认证接口,乱试会被风控拉黑)
  • 校验逻辑明显在服务端代码里(能 grep 源码就别瞎试)

标准操作流程(SOP)

1. 准备基准请求      → 一份"完整能跑通"的请求,状态码 200
2. 列出所有候选字段  → 请求头 / URL 参数 / 请求体所有 key
3. 单字段删减循环    → 每次只删一个字段,发请求,记录状态码
4. 单字段改值循环    → 对必需字段,改值看格式校验的边界
5. 复测与稳定性确认  → 关键结论各跑 2~3 次防偶发
6. 输出最小必需集    → 整理成"必需 / 可省 / 必需但值任意"三类

关键操作要点

① 一次只动一个变量 —— 同时改两个,结论就废了。这是控制变量法在协议层面的应用。

② 每个关键结论复测 ≥ 2 次 —— 网络偶发、上游限流、缓存命中都会污染单次结果。一次 200 不等于”通过”,连续 2 次 200 才算。

③ 看的是状态码 + 错误体,不是感觉 —— 200 400 403 429 500 503 520 每个码在不同代理里含义可能不一样,要看 body 里的 error 字段才能区分”真校验失败”还是”后端崩了”。

④ 删字段 vs 改值要分两步 —— 先用”删字段”找出必需项,再用”改值”找出每个必需项的格式边界(如长度、正则、枚举)。混着做会乱。


三大常见陷阱

陷阱 1:把”恰好能跑”当成”协议本意”

某天你测出 field_X 可以删,于是写代码不带 field_X下周服务端开了校验,你的代理全挂。

防御原则:代理实现应默认携带”完整最小必需集”,而非依赖某次测试得出的”可省略”结论。 测试结论只用来调试,不用来精简生产请求。

陷阱 2:状态码语义被中间层改写

经过反向代理(如 Cloudflare、Nginx、自研网关)的请求,状态码经常不是源服务给的:

  • 原本 400 可能被包装成 503520
  • 上游崩溃可能返回 500 但 body 里写的是 Panic detected,这不是参数错而是代码 bug
  • 限流 429 跟”账号被封” 403 在某些代理里都返回 503

心法:永远把状态码当成”线索”而不是”结论”,结论要靠 body 的 error message 来确认。

陷阱 3:灰度让结论”看起来矛盾”

同一个字段,A 同学测出”可省略”,B 同学测出”必需”,你以为有人搞错了——其实是服务端在分批灰度(部分账号、部分时段走新逻辑)。

应对

  • 多账号交叉验证
  • 时间隔开(早上一遍、晚上一遍)
  • 矛盾结论先记下来,标注时间戳和账号 ID,不要立刻覆盖旧结论

可迁移的场景

二分定位的核心不是”逆向 API”,是控制变量地缩小未知空间。它能直接迁移到:

场景怎么用
调试 LLM Prompt 不稳定从能跑的 Prompt 出发,每次只改一句,定位哪句在起作用
复现”偶发 bug”从能复现的最小输入出发,二分删行/删字段,缩到最小可复现 case
排查依赖冲突从能跑的 requirements.txt 出发,二分注释依赖找冲突项
排查 CSS 样式问题从能渲染的 DOM 出发,二分删除样式类找出哪条规则在生效
排查慢查询从慢 SQL 出发,二分删 JOIN / WHERE 条件找瓶颈

只要面对的是”黑盒 + 多变量 + 结果可观测”的局面,这个方法都成立。


配套工具与心智

工具

  • curl —— 最朴素的发请求工具,每次改一个字段最直观
  • HTTPie / Insomnia / Bruno —— 比 Postman 轻,适合写”参数化”的测试集
  • mitmproxy —— 抓真客户端的完整请求作为基准(最重要的一步是找到”完整能跑通”的样本)
  • 简单 shell 脚本 —— 字段多了就别手点,写个 for 循环跑全集

心智

  • 先抓基准,再做减法 —— 没有”完整能跑”的基准,所有删减实验都是无源之水
  • 承认接口会变 —— 写下结论时永远带日期。重读旧结论时先怀疑,再验证
  • 结论要可证伪 —— “这个字段必需”是个可证伪命题(删掉跑一次就行);“这个字段可能必需”不是

完整案例骨架(脱敏)

来源:2026-04 某中转 API 的逆向分析(详见 04_探索日志/ 对应日期)

目标:搞清楚某中转 API 校验了请求的哪些字段。

步骤

  1. 抓基准 —— 用官方客户端发一次成功请求,用抓包工具截下完整 HTTP(含所有头与 body)
  2. 删请求头 —— 完整请求 15 个头,每次只删一个,跑 2 次。结论:14 个可省,1 个必需(anthropic-beta
  3. 删 URL 参数 —— 测出 ?beta=true 这种 query string 可省略
  4. 删请求体顶层字段 —— 测出 system / metadata / messages / model / max_tokens 必需
  5. 拆 system 内部 —— system 是个数组,进一步测出第一个元素的 text 必须一字不差等于某固定字符串
  6. 拆 metadata 内部 —— 测出 user_id 必需且必须匹配特定正则
  7. 改值找格式边界 —— 把 user_id 改成各种值,确认是只校验格式还是校验具体值
  8. 汇总最小集 —— 输出一份”最小可通过请求”模板,标注每个字段为什么必需

用时:约 4 小时,发了约 200 个请求。

结论寿命:约 2 周后部分校验规则变更,需要重测。所以结论本身没多大长期价值,方法才有。


一句话收尾

黑盒接口面前,别猜,去测;别试一次,多测几次;别只看状态码,看 body。这套方法救过很多人也将救很多人。