Files
reverse-skills-max/vendor-new/xbsReverseSkill/web-js-env-patcher/references/xhr-fetch-semantics-audit.md

6.5 KiB
Raw Blame History

XHR/fetch 请求语义与生命周期审计

本文件用于解决“XHR/fetch 已桥接同一 TLS Session,但请求头、body、请求来源、失败语义、responseURL、事件顺序或 reload 生命周期仍错误”的问题。Session Bridge 只证明请求由哪个客户端发送;本审计证明发送的请求和浏览器行为是否正确。

核心原则

  • 真实请求前必须先执行 no-send 审计:Node bridge 记录完整待发送请求,但不得访问目标网络。
  • 浏览器成功样本与 Node no-send 结果必须生成可比较的 network transcript。
  • document navigation、reload、image、script、XHR、fetch、sendBeacon 必须有明确 actor,禁止根据 URL 猜请求类型。
  • 请求和响应的 matched 必须由 check_xhr_fetch_semantics.js 计算,不能手工填写。
  • Header 名称、顺序、重复字段和 body 原始字节属于请求语义,不得只比较对象键值。
  • 缺失字段不得在规范化时用 ""、0、main 或数组默认值掩盖。浏览器与 Node 同时缺失同一字段也必须失败。
  • transcript 中敏感 Header 可以只记录 { "redacted": true, "length": N, "sha256": "..." };不允许只保留 Header 名称或空字符串。

强制产物

case/fixtures/browser-network-transcript.ndjson
case/tmp/node-network-transcript.ndjson
case/tmp/xhr-fetch-semantics-audit.json
case/notes/xhr-fetch-semantics-diff.md

两份 transcript 都必须记录 baselineId。Node transcript 还必须记录:

{
  "type": "meta",
  "schemaVersion": "network-transcript/v3",
  "generatedBy": "runtime-network-recorder/v3",
  "baselineId": "fp-001",
  "mode": "no-send",
  "runtimeSourceHash": "...",
  "networkAttempts": 0
}

浏览器 meta 必须使用 mode: "browser-baseline",并记录 baselineId 与可信 generatedBy/capturedBy。Node meta 必须使用 mode: "no-send",记录当前源码的完整 SHA-256 runtimeSourceHash、可信 generatedBy 和显式 networkAttempts: 0。

每个事件共同字段

  • kind、pairKey、actor。
  • 显式 realmId、navigationEpoch、sequence,不能依赖默认值。
  • sessionId;同一 transcript 只允许一个 Session。

每个请求必须记录

  • method、完整 URL、query。
  • 有序 Header 数组,保留重复字段。
  • credentials、referrer、origin、Cookie 处理策略。
  • body 类型、编码、原始 byte length、SHA256。
  • Content-Type 和 multipart boundary。
  • redirect、cache、timeout、abort 信息。

POST、PUT、PATCH 或任何实际携带 body 的请求,必须存在 contentType、原始 bodyLength 和完整 bodySha256。没有请求体时也不得伪造调试字符串的 hash。

Header 推荐结构:

[
  ["content-type", "text/plain;charset=UTF-8"],
  ["cookie", {"redacted": true, "length": 128, "sha256": "..."}]
]

Header 数组中的每一项都必须有非空名称和真实值或脱敏摘要。比较时使用名称、顺序、重复次数、byte length 与 SHA-256;不能只比较名称集合。

浏览器与 Node 的原始 request id 可以不同,但必须在规范化阶段生成相同 pairKey。推荐使用“writer phase + actor + realm role + navigation epoch + 该 actor 第 N 次请求”,不得只用 URL 配对,因为同一 URL 可能同时用于 document navigation、reload 和 XHR。

每个响应必须记录

  • status、statusText、responseURL。
  • 响应头有序列表和脚本可见头列表。
  • body byte length、SHA256、responseType。
  • Set-Cookie 是否写入同一 session Cookie jar。
  • readyState、readystatechange、load、error、abort、timeout、loadend 顺序。
  • Promise resolve/reject 与 microtask/task 顺序。
  • PerformanceResourceTiming 可见时机。

即使响应体为空,也必须记录 bodyLength: 0 和空字节 SHA-256。status > 0 时必须记录最终 responseURL;失败、abort、CORS 或网络错误的 status=0 必须显式记录空 responseURL。

硬性浏览器语义

  • 网络错误、CORS 拒绝、abort 等 status=0 场景,responseURL 必须为空,除非同 baseline 浏览器证据明确相反。
  • completion 层不得用 request URL 为失败 XHR 回填 responseURL。
  • document navigation/reload 不得进入 XHR completion。
  • 首次 navigation 与 reload 必须创建各自正确的 document/realm 生命周期。
  • reload 或 navigation commit 后,旧 realm 的 pending timer、XHR、fetch、MessagePort task 和 observer callback 必须取消或丢弃。
  • 真实浏览器没有发出的 XHR/fetch,Node 不得多发;多余事件直接阻断。
  • getResponseHeader("set-cookie")、Response Headers 的 Set-Cookie 可见性必须以目标浏览器 baseline 为准。
  • browser/Node 必须逐事件保持 request、response、reload/navigation lifecycle 的全局顺序;只按 pairKey 分组后忽略相对顺序不能通过。
  • Node transcript 必须来自真正执行当前 XHR/fetch 实现的 recorder;手写与浏览器相同的 NDJSON、复制浏览器事件或空值占位都不算证据。

body 类型矩阵

目标路径涉及时至少覆盖:

  • null / undefined。
  • string。
  • URLSearchParams。
  • FormData 与 multipart boundary。
  • Blob。
  • ArrayBuffer / TypedArray。
  • Document。
  • JSON string。

必须比较发送前 body byte length 和 SHA256,不能只比较序列化后的调试文本。

Header 矩阵

必须检查:

  • setRequestHeader() 的 trim、合并和重复行为。
  • Header 名称大小写与发送顺序。
  • Header 实际值或脱敏后的 byte length / SHA-256;只有名称没有值直接失败。
  • 自动 Content-Type。
  • Cookie、Origin、Referer、Accept、Accept-Language、Client Hints 等由哪一层生成。
  • 禁止脚本设置的 Header 是否被正确过滤。
  • curl/HAR 与当前浏览器 baseline 冲突时,以当前 baseline 为准。

检查命令

node scripts/check_xhr_fetch_semantics.js --case-dir case --require --require-no-send --out case/tmp/xhr-fetch-semantics-audit.json --markdown
node scripts/check_xhr_fetch_session_bridge.js --case-dir case --require-live --tls-client curl_cffi --markdown
node scripts/check_environment_closure.js --case-dir case --before-real-request --require-live --tls-client curl_cffi --markdown

只有 no-send 语义审计和 session bridge 运行时审计同时通过,才允许少量授权真实请求。不得等用户提出“比较请求头和请求体”后才执行本流程。