rea-reverse

HAR 文件分析与脱敏:发给别人之前怎么查看、怎么去掉敏感信息

基于 REA 6.7.0发布者 rea-reverse更新于 约 8 分钟读完
本页目录

HAR 文件要先脱敏才能分享:Chrome 和 Edge 的 Export HAR (sanitized) 会去掉 Cookie、Set-Cookie 和 Authorization 请求头,但查询串里的 API key、其他鉴权头、表单提交内容和响应体都还留在文件里。先用脱敏方式导出,再按下面的清单把漏网的值搜一遍;或者干脆用 HAR 分析器 生成一份不含任何值的接口摘要发过去。本文依次讲各浏览器怎么导出、敏感信息藏在哪、怎样按接口读 HAR,以及怎样让 AI agent 逐条读取记录。

只抓你有权记录的流量(自己的应用、测试账号,或你获得授权测试的系统);HAR 里可能有他人的个人数据。

HAR 文件是什么

HAR(HTTP Archive)是一种 JSON 格式。顶层是 log,下面有 version(通常是 "1.2")、creator(写出文件的浏览器)和 entries。每个 entry 是一对请求和响应:

  • request:方法、带查询串的 URL、请求头、Cookie,以及请求体 postData
  • response:状态码、响应头、Cookie,以及 content,响应体放在 text 里(有时是 base64)
  • time 和 timings:请求耗时,分成 wait(首字节时间)等几个阶段

因为响应体通常也在里面,HAR 远不只是一串 URL。这正是它适合排查问题的原因,也是需要小心对待它的原因。

第 1 步:从浏览器导出 HAR

不管哪个浏览器,都要在复现问题之前打开开发者工具。Network 面板只在打开期间记录。

Chrome 和 Edge(Chromium 内核):

  1. 按 F12 打开 DevTools,切到 Network,勾选 Preserve log,避免页面跳转时清空列表。
  2. 复现问题。
  3. 点 Network 工具栏上的下载图标,选择 Export HAR (sanitized)…。

从 Chrome 130 开始,默认导出不包含 Cookie、Set-Cookie 和 Authorization 请求头。要导出它们,必须打开 Settings > Preferences > Network > Allow to generate HAR with sensitive data。提交工单时,几乎总是选脱敏版就够了。

Firefox: 打开开发者工具,切到 Network,页面会跳转的话打开 Persist Logs,复现问题后在请求列表上右键,选 Save All As HAR。Firefox 没有 Chrome 那一步脱敏,请当作它什么都包含。

Safari: 先在 Safari 设置里打开开发菜单(高级 > 显示网页开发者功能),打开 Web 检查器,切到 网络,复现问题后用 导出 保存 .har 文件。同样按未脱敏处理。

抓包越短越好。 在你关心的操作之前开始记录,操作完马上停止。短的抓包更好读,包含的个人数据也更少。

第 2 步:知道秘密藏在哪里

在归纳或分享 HAR 之前,先弄清敏感值通常在哪:

位置 常见内容 Chrome 脱敏导出会去掉吗?
Authorization 请求头 Bearer token、Basic 凭证 会
Cookie / Set-Cookie 头和 cookies 数组 会话 ID 会
其他请求头,如 X-Api-Key API key 不会
URL 查询串 ?token=、?api_key=、签名 URL 参数 不会
postData.text 登录表单里的密码、个人信息 不会
response.content.text 服务器返回的任何内容,包括用户资料 不会

最容易漏掉的是后三行。Chrome 脱敏导出的文件里,查询串中仍可能有 API key,响应体里仍可能有完整的用户资料。

第 3 步:按接口归纳 HAR

400 条记录的 HAR 用文本编辑器很难看。通常你想要的是一张接口清单:每个接口调用了几次、返回了哪些状态码、耗时多少。打开 HAR 分析器,把文件拖进去即可。文件在当前标签页的 Web Worker 里解析,Worker 的内容安全策略禁止任何网络请求,所以不会上传。

为了展示真实结果,我们给一个演示用的笔记应用 notes.example.com 构造了一份小 HAR,共 7 条:两次读取笔记、一次新建笔记、一次返回 500 的搜索、一次 GraphQL 查询,以及两个脚本,其中一个来自第三方域名。部分请求带着 JWT Bearer token 和会话 Cookie,搜索 URL 里带着 api_key=test-key-123。下面是分析器导出的 JSON 片段:

{
  "summary": {
    "exchanges": 7,
    "endpoints": 6,
    "firstParty": "example.com",
    "thirdPartyRatio": 0.143
  },
  "endpoints": [
    {
      "host": "notes.example.com",
      "method": "GET",
      "path": "/api/notes/{id}",
      "count": 2,
      "status": { "200": 2 },
      "credentials": [
        { "where": "header", "name": "authorization", "value": "[REDACTED]",
          "length": 60, "scheme": "Bearer", "jwt": true },
        { "where": "cookie", "name": "sid", "value": "[REDACTED]", "length": 20 }
      ],
      "maxMs": 2400,
      "avgMs": 1242.5,
      "resShape": { "object": { "id": "number", "title": "string",
        "tags": { "array": "string" }, "archived": "boolean|null" } }
    },
    {
      "host": "notes.example.com",
      "method": "GET",
      "path": "/api/search",
      "status": { "500": 1 },
      "query": ["api_key", "q"],
      "credentials": [
        { "where": "query", "name": "api_key", "value": "[REDACTED]", "length": 12 }
      ],
      "maxMs": 310
    }
  ]
}

怎么读:

  • 路径模板把重复调用合并。 /api/notes/41 和 /api/notes/42 合成了一行 /api/notes/{id},count: 2。纯数字、UUID、JWT、日期、长十六进制串、邮箱和类似 token 的随机串都会换成占位符;order-12345 这样的 slug 保持原样。
  • 把 maxMs 和 avgMs 放在一起看,就能找到慢请求。 平均 1242.5 ms 掩盖了实情:两次读取里有一次花了 2400 ms。某个接口的最大值远高于平均值时,先查它。
  • status 按接口统计错误。 搜索接口返回了 500。状态码 0 表示请求没有收到响应,例如被拦截或取消。
  • resShape 只描述结构,不含数据。 能看出 archived 有时是布尔值、有时是 null,前端 bug 常常就出在这种差异上。
  • GraphQL 请求按操作拆开。 演示里的 /graphql 单独成为一个接口,"kind": "graphql"、"operation": "query Me",查询文本和变量都没有保留。

我们还在完整导出里搜索了演示文件中的每个秘密:JWT、Cookie 值、test-key-123、搜索词和笔记正文,一个都没找到。分析器从不把请求头、Cookie、查询参数或请求/响应体的值写进报告,只保留名称、类型、次数和最长值的长度。

第 4 步:决定分享什么

分析器不会生成一份清理过的 HAR,它生成的是摘要。所以你有三种选择,按安全程度排序:

  1. 分享分析器导出的 JSON。 里面没有任何值,只有域名、路径模板、名称、类型、次数和耗时,但会带上你打开的文件名。回答“哪个接口出错、有多慢”已经够用。
  2. 分享浏览器的脱敏导出。 用 Chrome 或 Edge 的 Export HAR (sanitized),然后按下面的清单自己检查查询串里的 API key 和请求/响应体里的个人数据。
  3. 只有对方确实需要完整的头和正文时,才分享完整 HAR,并通过公司认可的渠道发送。发完之后退出这个测试会话,让里面的 token 失效。

分享前检查清单:

  • 在文件里搜索 token、key、secret、password、session 和 eyJ(大多数 JWT 的开头)。
  • 搜索你自己的邮箱、姓名和手机号。
  • 检查登录、注册请求的 postData.text。
  • 看看抓包里有没有其他标签页或无关网站的请求。
  • 尽量用测试账号。真实账号的 token 在过期或退出登录之前一直有效。

分析器的凭证规则覆盖哪些: 名为 authorization、proxy-authorization、cookie、set-cookie、x-api-key 的请求头;名称里含 token、secret、password、session、key、auth、signature、credential 的请求头或查询参数;以及值是 JWT 的任何请求头或查询参数。这些会作为凭证按名称列出。其他值同样会被丢弃,因为报告根本不存值。分析器做不到的是判断某个正文字段里是不是个人数据,它只能告诉你 title 字段是字符串。

第 5 步:用 REA 让 AI agent 读 HAR

摘要告诉你该看哪一条请求。要看这条请求真实的头和正文,或者让 agent 把请求和前端代码对照,就用 REA(第三方开源逆向命令行工具,安装见 REA MCP 安装指南)的 inspect-web-network-capture。它不需要浏览器,也从不访问录制里的 URL。

rea inspect-web-network-capture /absolute/path/notes-demo.har har --record 1 --json

--record 接受从 0 开始的条目序号,可以重复写多次;不写就读取全部条目。下面是 REA 对演示文件第 1 条返回结果的一部分:

"records": [
  {
    "ordinal": 1,
    "location": { "kind": "json-pointer", "pointer": "/log/entries/1" },
    "reported": {
      "request": {
        "method": "GET",
        "url": "https://notes.example.com/api/notes/41",
        "headers": [
          { "name": "authorization", "value": null },
          { "name": "cookie", "value": null }
        ]
      },
      "response": {
        "status": 200,
        "content": {
          "mimeType": "application/json",
          "text": "{\"id\":41,\"title\":\"a\",\"tags\":[\"x\"],\"archived\":false}"
        }
      }
    },
    "redactions": [
      { "pointer": "/request/headers/0/value", "reason": "transport-credential" },
      { "pointer": "/request/headers/1/value", "reason": "transport-credential" }
    ]
  }
]

它和分析器是两类工具。REA 按浏览器写出的原样保留记录,包括响应体,并记录 Evidence ID 和文件的 SHA-256,agent 可以准确说明某个事实来自哪个文件的哪一条。它只去掉已知的传输凭证:Authorization、Proxy-Authorization、Cookie、Set-Cookie 请求头的值,Cookie 值,以及 URL 里嵌入的用户名和密码。

其余内容都会保留。我们读取第 4 条(那条失败的搜索)时,URL 原样返回 https://notes.example.com/api/search?q=invoice&api_key=test-key-123,redactions 是空列表。要排除这类值,需要显式声明:

rea inspect-web-network-capture /absolute/path/notes-demo.har har \
  --record 4 --sensitive-value 'test-key-123' --json

加上这个参数后,REA 把整个 url 字段换成了 null,并记录 {"pointer": "/request/url", "reason": "explicit-sensitive-value"}。输出里任何地方都找不到这个 key,Evidence 参数里也只记 "sensitive_value_count": 1,不记值本身。注意它丢掉的是整个字段而不是匹配的那一段,所以这条记录的 URL 其余部分也看不到了。

通过 MCP 调用时,agent 使用 inspect_web_network_capture,传入 capture_path、format: "har",可选 record_ordinals 和 sensitive_values。提示词可以这样写:

用 REA 读取 /absolute/path/notes-demo.har。找出失败或耗时超过一秒的请求,
给我看它们的请求和响应,并注明每条的 record ordinal。把 test-key-123 当作敏感值。

REA 也能用 api-surface 视图把 HAR 按接口分组。CLI 的每条命令是独立进程,所以要把完整 Evidence 以 inline 方式交给视图:

rea inspect-web-network-capture /absolute/path/notes-demo.har har --json > har-evidence.json
jq '{source: {kind: "inline", evidence: .}, view: {kind: "api-surface", offset: 0, limit: 50}}' \
  har-evidence.json > api-surface-request.json
rea inspect-analysis-view /absolute/path/api-surface-request.json --json

和上面的分析器在同一份演示文件上对比:它把 /api/notes/41 和 /42 合并成 /api/notes/{numeric},其余 5 条各成一组;但没有耗时、凭证列表和响应结构,/graphql 也只是一个 POST 分组。它的长处是给出 record ordinal,可以直接填回 --record。它不保留查询参数的值(输出里没有 test-key-123),但会留下被模板化的路径段原值("41"、"42"),所以路径里带 ID 时要当敏感数据处理。走 MCP 时先用 detail: "summary",再把返回的 parent_evidence_id 作为 retained-evidence 来源交给视图:我们实测回复从 16,948 字节降到 4,072 字节。在 CLI 上这种引用会报 evidence_integrity_mismatch,因为每条命令的进程都已退出,所以要以 inline 方式传入 Evidence。

同一条命令也能读 Fiddler 的 .saz 归档(... notes-demo.saz saz --record 1 --json),凭证置空和 --sensitive-value 的处理与 HAR 相同。我们用按 Fiddler 的 raw/NN_c.txt / NN_s.txt / NN_m.xml 结构自己打的 SAZ 测过。记录结构和 HAR 不同:ordinal 从 0 开始,而 Fiddler 的会话号从 01 开始;状态码是字符串("200");header 值保留冒号后的空格;正文是原始字节,不做 chunked 还原也不解压;api-surface 对 SAZ 的 Evidence 会返回 unsupported_target。

常见问题和排错

分析器提示 JSON 不是 HAR 1.2 格式。 文件缺少 log.entries,或者某条记录没有请求方法、URL 或响应状态码。请从浏览器重新导出,不要手工改。

明明需要登录的接口,凭证列表却是空的。 你的 HAR 很可能来自 Chrome 的脱敏导出,Authorization 和 Cookie 在分析器看到之前就被删掉了。用于分享这没问题。要审计哪些接口带了凭证,就用包含敏感数据的导出,在本地分析,用完删掉。

REA 拒绝读取文件。 HAR 必须是 1.2 版,有重复 JSON 键的会被拒绝;SAZ 不能加密。两种格式的输入上限都是 32 MiB。请重新导出一份只覆盖目标操作的短抓包。

同一个接口出现了两次。 路径里有某一段被占位规则当成普通单词,例如 order-12345。每一行本身是对的,只是没有合并。

FAQ

不用任何工具能看 HAR 吗?

可以,它就是 JSON。jq '.log.entries[] | [.request.method, .request.url, .response.status, .time]' file.har 就能得到一张简单的表。不过要分享结果,还是需要路径模板和脱敏。

这篇和“追踪 API 调用到代码”有什么区别?

本文讲 HAR 文件本身。要从一个请求找到发出它的 JavaScript,可以先看浏览器开发者工具 Network 面板里的 Initiator(发起者)列,它会显示发出每个请求的脚本和调用栈。