HAR 文件分析与脱敏:发给别人之前怎么查看、怎么去掉敏感信息
本页目录
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,以及请求体postDataresponse:状态码、响应头、Cookie,以及content,响应体放在text里(有时是 base64)time和timings:请求耗时,分成wait(首字节时间)等几个阶段
因为响应体通常也在里面,HAR 远不只是一串 URL。这正是它适合排查问题的原因,也是需要小心对待它的原因。
第 1 步:从浏览器导出 HAR
不管哪个浏览器,都要在复现问题之前打开开发者工具。Network 面板只在打开期间记录。
Chrome 和 Edge(Chromium 内核):
- 按 F12 打开 DevTools,切到 Network,勾选 Preserve log,避免页面跳转时清空列表。
- 复现问题。
- 点 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,它生成的是摘要。所以你有三种选择,按安全程度排序:
- 分享分析器导出的 JSON。 里面没有任何值,只有域名、路径模板、名称、类型、次数和耗时,但会带上你打开的文件名。回答“哪个接口出错、有多慢”已经够用。
- 分享浏览器的脱敏导出。 用 Chrome 或 Edge 的 Export HAR (sanitized),然后按下面的清单自己检查查询串里的 API key 和请求/响应体里的个人数据。
- 只有对方确实需要完整的头和正文时,才分享完整 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(发起者)列,它会显示发出每个请求的脚本和调用栈。