什么是客户端加密?浏览器完成加密工作
客户端加密意味着文件在上传前在浏览器中加密。了解为什么这种方式能给您最大的隐私和控制。
客户端加密的意思是:你的浏览器或应用在设备上完成文件加密,数据触网之前一切都已加密完毕。服务器收到的只是密文——AES-256-GCM 的输出,与随机噪声无法区分——解密密钥从不离开客户端。这与服务端加密截然相反:服务端加密意味着服务商持有密钥,理论上可以读取你的文件。Web Crypto API(window.crypto.subtle)让任何现代浏览器都无需插件即可完成加密,在 AES-NI 硬件上速度约为每秒 2–3 GB。HexaTransfer、SwissTransfer、Tresorit Send 和 Proton Drive 均采用此模型,确保即便服务本身被攻陷,文件也能保持私密。
浏览器成为加密引擎
几年前,真正的加密需要安装桌面应用或在命令行使用 PGP。2017 年由 W3C 标准化的 Web Crypto API 改变了这一切。它向任何主流浏览器(Chrome、Firefox、Safari、Edge)中运行的 JavaScript 直接暴露了 AES-GCM、RSA-OAEP、ECDH、HMAC、PBKDF2 和 SHA-256 等算法。
性能不再是瓶颈。Intel AES-NI 指令集让 AES-256-GCM 每核心达到 3–5 GB/s,Apple M 系列和高通骁龙芯片的 ARM 加密扩展提供同等吞吐量。在中端笔记本上,加密 1 GB 文件只需约 300–500 毫秒。
剩余的挑战是处理大于浏览器内存的文件。Streams API 和 ReadableStream 允许代码以 4 MB 的分块处理文件,每个分块使用独立的计数器模式 IV 单独加密。这正是服务将上限推至 10 GB 甚至更多的方式。
典型的客户端加密流程
以下是基于浏览器的服务通常执行的步骤:
// 1. 生成随机 256 位 AES 密钥
const key = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"]
);
// 2. 按分块读取文件
const file = fileInput.files[0];
const chunkSize = 4 * 1024 * 1024;
// 3. 用唯一的 12 字节 IV 加密每个分块
for (let offset = 0; offset < file.size; offset += chunkSize) {
const chunk = file.slice(offset, offset + chunkSize);
const iv = crypto.getRandomValues(new Uint8Array(12));
const ciphertext = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv }, key, await chunk.arrayBuffer()
);
// 4. 将 [iv || ciphertext] 上传到服务器
}
// 5. 导出密钥并嵌入分享 URL 的片段部分
const keyBytes = await crypto.subtle.exportKey("raw", key);
const shareUrl = `https://example.com/d/${fileId}#k=${base64url(keyBytes)}`;
服务器看到的是随机字节、文件 ID,仅此而已。密钥只存在于用户的浏览器内存和 URL 片段中。
为何优于服务端加密
服务端加密意味着服务商会根据需要解密——生成缩略图、执行病毒扫描、处理搜索查询,或响应法律要求。2023 年 Apple 披露,iCloud 备份(在高级数据保护功能推出之前没有端到端加密)可供 Apple 访问,因此也可依据有效请求提供给美国执法部门。
客户端加密改变了这一切。由于密钥从不到达服务商:
- 内鬼员工看不到任何东西。拥有数据库访问权限的工程师只能得到密文。
- 传票只能得到密文。服务商可以遵守令状,提交加密块,但没有密钥则毫无用处。
- 数据泄露只泄露密文。2021 年 LastPass 事件证明了这一点——被盗的密码库已加密,只有主密码较弱的用户才面临真实风险。
- 服务商停运不影响你的数据。即使公司倒闭,你本地持有密钥(即 URL),仍然可以解密文件。
服务器仍然能看到什么
客户端加密保护文件内容,但并非一切。服务器通常仍能观察到:
- 文件大小——密文长度近似明文长度(AES-GCM 每次加密额外增加 16 字节认证标签加 12 字节 IV)。
- 上传和下载 IP 地址及时间戳。
- TLS 握手的会话元数据,包括客户端的 TLS 指纹。
- 明文文件名——除非文件名也被包含在加密载荷中,否则可能泄露。
良好的客户端服务会将文件名作为密文头的一部分加密,并将文件大小填充至固定区间(1 MB、10 MB、100 MB)以模糊大小。Tresorit 和 Proton Drive 明确记录了其元数据暴露情况。
带密码的客户端加密
许多服务允许用户在 URL 片段之上额外添加密码。流程如下:
- 浏览器生成随机 128 位盐值,通过 PBKDF2-HMAC-SHA-256(60 万次迭代,OWASP 2023 年推荐)或 Argon2id(
memory=64 MB, iterations=3)派生密钥。 - 使用派生密钥加密文件。
- 盐值放入 URL 片段;密码通过带外渠道传递。
- 收件方输入密码,在本地重新派生密钥。
这将单通道分享(URL 即足够)变为双因素:攻击者需要同时获得链接和密码。PBKDF2 在 60 万次迭代下,使用现代 GPU 每猜测一次需要约 10 秒,因此密码需要至少 40 位熵才能抵御有决心的攻击者——意味着至少 10 个字符,且来自多样化的字符集。
信任的转移:从服务到客户端代码
客户端加密改变了信任边界。此前,你信任服务妥善处理你的明文。现在,你信任服务在每次页面加载时向你的浏览器下发的 JavaScript。恶意的代码更新可能在加密前或加密过程中泄露密钥。
目前有三种缓解措施,各有其严格程度:
- 子资源完整性(SRI):为脚本标签确保 JS 哈希与已知值匹配。
- 代码审计:由 Cure53、NCC Group 或 Trail of Bits 等机构验证加密逻辑的正确性。
- 可重现构建:允许独立方确认发布的代码与公开源代码一致。
- 内容安全策略(CSP)头:阻止可能篡改加密逻辑的第三方脚本。
最严格的方法(Proton Mail 的 pmcrypto 和部分基于 Electron 的客户端采用)是分发签名的二进制文件,而非每次访问都刷新的 JavaScript。基于浏览器的服务用零安装的便利性换取了部分严格性。
客户端加密最能发挥价值的场景
以下几种情况值得为略微增加的初次加载时间买单:
- 法律和医疗文件。HIPAA 45 CFR § 164.312 和律师-客户特权都非常适合服务商盲目架构。
- 新闻报道与线人保护。发送未经编辑的文件时,即使元数据泄露也可能带来风险。
- 企业知识产权。董事会材料、财务模型、并购文件,其中文件传输服务商的内部威胁是现实风险。
- 个人重要记录。税务文件、护照、体检结果——你不希望在服务商泄露事件的新闻中看到这些文件。
对于低敏感度文件(会议照片、食谱等),传统的服务端加密完全够用。
如何辨别真正的客户端加密
四个标志表明服务确实执行了客户端加密:
- URL 片段包含密钥。分享链接
#之后有看起来像 base64 随机字节的文本。 - 上传的是密文。在上传时打开 DevTools → Network,请求体应看起来像随机字节,而非你的文件名。
- 大文件仍然运行顺畅。真正的客户端加密通过流式分块处理,不会重新上传到服务端加密网关。
- 隐私政策说明"我们无法解密你的文件"。配合技术白皮书,而非仅是营销文案。
能通过以上所有测试的服务:SwissTransfer E2EE 档位、Tresorit Send、Proton Drive 分享链接、Mega.nz 和 HexaTransfer。不能通过的服务:WeTransfer(标准档)、Google Drive、Dropbox 分享链接。
在 hexatransfer.com 上试试 — 免费、无需注册、最多 10 GB。