隐私

浏览器端文件转换:工作原理及为何你的文件始终私密

koboshiCo-founder
·2 分钟阅读
浏览器端文件转换:工作原理及为何你的文件始终私密
概述

基于服务器的转换器要求你把文件上传到别人的机器上。浏览器端转换器使用 WebAssembly 和平台 API 完全在你的设备上运行。本文详解使这一切成为可能的架构,以及它对隐私意味着什么。

把文件上传到一个基于服务器的转换器,三件事会发生。你的文件通过网络传输到你无法控制的某个 IP 地址。服务器上的某个进程解码它。然后,取决于网站的保留策略,文件会留在磁盘上——短则几分钟,长则永久。

同样的事情在浏览器标签页里做,文件不会离开你机器的内存。解码器运行在浏览器引擎强制执行的 WebAssembly 沙箱内。网络从未被触及。

这不仅是策略差异,更是架构差异。基于服务器的转换器可以承诺删除你的文件;浏览器端的转换器无法泄露你的文件,因为它从一开始就从未收到过它们。

架构

客户端转换由四个层次组成:

第 1 层:文件访问

当你拖放一个文件到浏览器标签页时,DragEvent<input type="file"> 会给 JavaScript 一个 File 对象。File 不是文件的内容,而是一个引用:包含文件名、大小、MIME 类型,以及一个按需从磁盘读取字节到内存的方法(file.arrayBuffer())。

在这个方法被调用之前,零字节被移动。文件安静地躺在你的文件系统里。浏览器的文件选择器是操作系统级别的对话框,网页只能看到用户明确选择的 File 对象。

第 2 层:格式检测

任何文件的前几个字节都能可靠地标识其格式。JPEG 以 FF D8 FF 开头。PNG 以 89 50 4E 47 开头。HEIC 文件包含一个 ftyp box,在偏移量 8 处带有 heicheif 品牌标记。

读取文件头(通常是前 32 个字节)足以确认它到底是什么,无论扩展名是什么。这项检查在任何解码器接触像素数据之前执行。如果文件头不匹配,文件立即被拒绝。没有浪费的 CPU 周期,也不会在解码到一半时弹出令人困惑的错误信息。

第 3 层:解码

原始字节通过解码器变成像素。用哪个解码器取决于格式:

  • JPEG、PNG、WebP、BMP:浏览器自带这些格式的原生解码器。createImageBitmap() 将压缩字节交给操作系统的编解码器(Windows 上是 Windows Imaging Component,macOS 上是 Core Graphics,Linux/ChromeOS 上是 Skia),返回原始像素数据。这条路径速度快,在操作系统支持的地方有硬件加速,且不需要额外代码。
  • HEIC:除 Safari 外,没有浏览器自带 HEIC 原生解码器。我们的转换器内置了 libheif——一个编译为 WebAssembly 的 C 库。.wasm 二进制文件(~1.2 MB 压缩后)下载一次后缓存。它在 WASM 沙箱内将 HEIF/HEIC 文件解码为原始 RGB 像素。
  • PDF:PDF.js(Mozilla 的 PDF 渲染器)将每一页以请求的分辨率渲染到画布。没有服务端渲染。PDF 从未离开浏览器。

每个解码器从内存读取、向内存写入。没有解码器会打开 socket。WASM 沙箱尤其无法发起网络请求:浏览器不会向 WebAssembly 模块暴露网络 API。即使 C 代码调用了 socket(),沙箱也会拦截它。

第 4 层:编码

原始像素进入 <canvas> 元素的 2D 上下文。canvas.toBlob()canvas.toDataURL() 调用浏览器的内置编码器输出 JPG、PNG 或 WebP。编码器是平台原生代码:和你的操作系统保存截图用的是同一条代码路径。

输出是一个 Blob——内存中的字节缓冲区。它被交给下载链接,或者打包进一个用 JavaScript 构建的 ZIP 压缩包。在整个过程中,没有任何字节触碰到网络 socket。

浏览器强制执行了什么

网页运行在浏览器在引擎层面维护的沙箱内。这不是礼貌协议,而是由进程隔离强制执行的:

渲染器进程:JavaScript 引擎、DOM 和 WASM 运行时运行在一个沙箱化进程中,没有直接的文件系统或网络访问权限。它通过 IPC 与浏览器进程通信。

站点隔离:现代浏览器将每个来源放在自己独立的渲染器进程中。你在 file-convert-factory.org 中的文件对于运行在任何其他域名上的 JavaScript 是不可见的。

WASM 沙箱:WebAssembly 模块只能看到一块平坦的线性内存缓冲区,别无他物。没有文件系统 API,没有 fetch(除非 JavaScript 显式导入),没有访问 DOM 或其他浏览器 API 的权限。一个被攻破的 WASM 模块最坏的情况是破坏自己的内存,让标签页崩溃。

相比之下,基于服务器的转换器以特权进程运行在服务器的操作系统上。它可以读磁盘、写磁盘、打开网络连接、派生子进程。安全模型取决于服务器运营者的能力和意图。浏览器沙箱只取决于浏览器引擎的正确性,而浏览器沙箱是软件领域中被审计最严格的安全边界之一。

基于服务器的转换器承诺(和不承诺)什么

基于服务器的转换器本身并不恶毒。很多是由善意的团队运营的。问题出在结构上:

  1. 上传步骤就是一次复制。你的文件现在存在于两个地方。你控制一个副本,别人控制另一个。
  2. 删除是一个承诺。"我们 24 小时后删除文件"意味着你要信任那行日志、那个定时任务、备份系统和每个有服务器权限的员工。这些都无法从外部验证。
  3. 元数据随文件一同传输。你照片的 EXIF 数据(GPS 坐标、相机序列号、时间戳)属于文件字节的一部分。文件上传了,元数据也就上传了。我们的 EXIF 隐私指南详细介绍了里面到底有什么以及如何去除。简单来说:本站的每个转换器都会去除元数据,因为它解码像素再重新编码,丢弃了所有不属于图像数据的内容。
  4. HTTPS 保护的是传输管道,不是端点。TLS 加密上传过程中的数据,但对于文件到达后发生的事情无能为力。

浏览器端转换器的隐私保证更窄但更强。它说的是:你的文件不会离开你的设备,因为不存在让它们离开的代码路径。这是可验证的。转换文件时打开 DevTools 的 Network(网络)面板,你会看到 WASM 二进制文件加载一次后就再也没有任何网络请求。零字节上传。没有对 /api/convert 的请求。没有 WebSocket 流量。转换完全在渲染器进程内发生。

亲自验证

你不必相信任何人的说辞。打开 Chrome DevTools(F12),切换到 Network 面板,然后在任何工具页面上转换一个文件。你能看到的唯一网络活动:

  1. 页面 HTML、CSS 和 JavaScript:首次访问时加载一次。
  2. WASM 二进制文件(针对 HEIC 转换器):加载一次,之后缓存。
  3. 分析请求(如果你没有屏蔽的话)。

没有任何文件数据离开浏览器。每个请求的 Content-Length 单位是 KB,不是 MB。任何会用 DevTools 的人都可以独立验证这一点。

对于基于服务器的服务,你无法做同样的断言。你上传文件,得到结果,然后你只能相信服务器确实删除了它。

相关工具

本站的每一个转换器都遵循这套架构。流水线(拖放、验证、解码、编码、下载)在所有 23 个工具上运行方式完全一致:

关于 WASM 沙箱的技术细节,参见我们的 WebAssembly 详解。想深入了解你照片携带了什么元数据,EXIF 隐私指南会逐字节地教你读取和去除它们。

更多推荐阅读