浏览器里做加密,怎么当场核对明文没有离开本机

页面上写「本地加密」「不上传」,本身无法证明。打开开发者工具的 Network,用一枚只有这次实验才有的金丝雀字符串去搜:请求行、请求体和分析上报里都不该出现明文、口令或井号后面的密钥。AES-256-GCM 应在本机 Web Crypto 算完,才允许密文出站。

口号无法核对,流量可以

很多人判断一个「在线加密」页是否安全,靠的是页面上的一句话:本地计算、零上传、端到端。这些句子可以出现在任何站点上,包括把文件直接 POST 到服务器再代为加密的站点。它们不是证据。

你真正能当场看到的,是当前标签页发出了哪些请求。浏览器开发者工具的 Network 面板会列出方法、地址、查询串、请求体,以及部分访问统计。如果你刚选中的文件名、口令,或一段只有你知道的「金丝雀」明文出现在这些位置,所谓本地加密就不成立。

反过来,Network 里没有明文,只能说明这一次操作没有把这些字段当作业务数据发出去。它不能证明内存里没有明文、扩展程序没有读取剪贴板,也不能证明下一次版本更新后行为不变。核对的价值在于:把不可检验的宣传,收成一次可重复的观察。

「本地」具体指哪一层计算

「浏览器本地」不是「这个域名看起来很安全」,而是「加解密发生在你正在看的这个标签页里」。对 AES-256-GCM 来说,常见的正确路径是调用 Web Crypto API:密钥派生、加密、认证标签都在浏览器提供的密码学接口里完成,而不是把原文交给远端接口代算。

文件加密的典型过程是:你通过文件选择器选出一个本机文件,脚本把它读成内存中的二进制块,按块加密,再触发浏览器下载密文。下载下来的 .lock.enc 是结果文件,不是上传回执。单文件上限如果写成 5 GB,指的是本机流式处理能力,不表示服务器收了 5 GB 原文。

还要把「业务上传」和「页面自己会发出的请求」分开。打开即用的工具站通常仍会加载样式和脚本,也可能发送不含正文的访问统计。这些请求的存在,不能用来指控「文件被上传了」;但统计请求的 query 或 body 里如果出现了你刚输入的口令或待测密码,那就是另一回事。

把计算边界写成可核对的句子,会比形容词更有用:密码生成、密码检测、隐私清洗和文件加解密的明文与密钥默认不离开浏览器;阅后即焚只允许密文出站,解密密钥放在 URL 的 # 片段。MakePwd 按这个边界实现工具,并且全部打开即用、没有账号。承诺仍然只是承诺。下面用 Network 把它收成检查项。

不要用真实密钥、证件或未脱敏表格做实验。准备一个可以丢弃的小文件,口令用一次性长句。核对的是流量,不是把隐私再暴露一遍。

用 Network 做一次可重复检查

先准备一枚不会出现在真实业务里的标记。文件名可以用 canary-local-2026.xlsx,口令用一段随机长句,正文里写一句只有这次实验才有的话。金丝雀的作用是搜索:在 Network 的过滤器里粘贴它,命中即失败。

打开开发者工具,切到 Network,勾选 Preserve log,过滤 XHR / Fetch。不要只看「成功的请求」——被取消或返回 4xx 的请求同样可能已经带上了明文。

然后做一次完整操作:选文件、输入口令、点加密或生成。结束后先不要关面板,按下面三处看。

  1. 在过滤器里粘贴金丝雀字符串,先看有没有红色命中。有命中就停下来读那条请求,不必再往下做「感觉上像本地」的判断。
  2. 没有命中时,再逐条打开 XHR / Fetch,对照请求行、查询串和请求体。静态资源、字体和脚本可以忽略。
  3. 单独过滤访问统计路径,打开 query 与 body。页面标题和路径可以出现;刚输入的口令、待测密码、清洗前原文和文件内容不应出现。

看请求行

完整 URL 里的 path 和 ? 后面的查询值得逐字看。id 这类定位字段可以出现;文件名、口令、待测密码、# 后面的密钥不应该出现。把地址栏整串和请求行对比:井号之后的部分如果进了请求行,说明实现把 fragment 错用成了 query,或脚本主动读取后写进了请求。

看请求体

POST / PUT 的 payload 是第二处。文件加密如果声称本地完成,请求体里不该有原文件二进制,也不该有口令。阅后即焚可以有密文字段,那是预期出站的数据;你要确认它看起来不像你刚输入的原文。密码检测页如果把待测密码 POST 出去,无论目的写成「查泄露」还是「算强度」,都已经离开了本机。

单独看分析上报

访问统计经常被忽略。主接口干净、上报却带着输入框全文,这次操作仍不能算「明文留在浏览器」。过滤统计路径时,不要假设「分析脚本一定无害」——它只是另一类出站请求,检查方式与业务接口相同。

勾选 Preserve log。加密完成后如果页面跳转或刷新,未勾选时第一条带明文的请求可能已经被清掉,你会得到虚假的「面板是空的」。

问号会进 HTTP,井号默认不会

URL 有两段常被混在一起。问号后面的查询会进入 HTTP 请求行,服务器、反向代理和访问日志都能看见。井号后面的片段默认由浏览器留在本地,用来给当前页的脚本读,不随这次文档请求发给服务器。

因此,一次性密文链接如果把密钥放进 #,接收方打开 s.html?id=...#密钥 时,服务器按设计只能看到 id,看不到密钥。这不是额外的加密协议,而是浏览器对 fragment 的默认行为。它有边界:你把完整地址贴进工单、群聊或某些会丢掉 hash 的跳转页,密钥就从「不进 HTTP」变成「出现在别人的屏幕和日志里」。

核对方法同样具体:创建一条测试焚链,看创建请求的 body 是否只有密文;打开阅读页时,看文档请求和后续接口的 URL 是否只含 id。地址栏里 # 后面的内容不应出现在这些请求里。阅读页对接收方公开,不需要登录。

看哪里 会不会进 HTTP 怎样算通过
页面口号 不涉及 不能当证据,只作对照
请求行 / query 无金丝雀、无口令、无 fragment 密钥
POST body 无原文;焚链只允许密文
URL # 片段 默认不会 地址栏有、请求行没有
访问统计 看实现 无输入框原文

你能证明什么,不能证明什么

这次检查能支持的结论很窄,写清楚反而更有用。

能支持:在你使用的这个浏览器、这个版本、这一次操作里,明文、口令和 fragment 密钥没有作为已观察到的 HTTP 业务数据或分析原文离开标签页。

不能支持:没有别的标签页或扩展在读剪贴板;硬盘上的下载目录是安全的;对方收到密文后不会截图;密码检测覆盖了全网泄露库。如果检测只做本地熵和一份公开弱口令 Top 列表,它只能回答「像不像常见弱口令」,不能回答「从未出现在某次泄露中」。那不是全网 HIBP 撞库。范围怎么写、怎么当场核对名单行数,见本地弱口令名单能证明什么,为什么它不是全网泄露查询

也不要把它理解成渗透测试。你没有检查 WebSocket、Service Worker 缓存,也没有逆向混淆脚本。目标是让自己能向同事解释:我打开了 Network,用金丝雀搜过,请求行和 body 是干净的。这比转发一句「官网说不上传」更接近工程讨论。

把同一套步骤用在打开即用的工具上

若你希望用一个把计算边界写清楚的页面来练习,可以从 MakePwd 的文件加密盒开始。打开即可使用,无需注册。选一个不含真实隐私的小文件,口令用金丝雀,加密后下载 .lock。同时盯着 Network:你应看到静态资源和可能的访问统计,不应看到原文件或口令作为业务字段。算法按站点说明是 AES-256-GCM,在 Web Crypto 里算,单文件不超过 5 GB。

阅后即焚适合练第二项:创建一条无害测试文本,确认出站的是密文;阅读页对接收方公开,链接形态是 s.html?id={id}#{key}。密码检测适合练「输入框内容会不会进统计」——待测密码按产品说明不上传,比对发生在本地名单。

这些练习的目的不是证明某一个站点「绝对安全」,而是让你把同一套核对步骤用熟。换任何声称本地加密的页面,步骤不变:金丝雀、Preserve log、请求行、请求体、统计上报。

下次核对时记住的三件事

第一,看流量,不看口号。第二,密文出站可以接受,密钥和原文不行。第三,换浏览器、换版本、换功能后重新做一遍金丝雀搜索。能重复的观察,才值得写进你们自己的安全说明。

如果你还要继续追问「为什么密钥可以放在井号后面」,请读URL 的井号后面为什么适合放密钥,以及什么时候会失效。本文只把「明文有没有离开这个标签页」收成可以当场做完的检查。