纯浏览器加密,零依赖实现秘密分享
用浏览器原生 API 做端到端加密分享敏感信息
说实话,网上传个密码真是让人提心吊胆。
邮件发完赶紧回去删,Slack 传完后悔一辈子,用第三方工具又怕人家后台看你的数据。有没有一种更靠谱的方式?
还真有。而且用浏览器就能实现。
第三方服务的问题在哪
OneTimeSecret 这类工具开创了"阅后即焚"的玩法。粘贴秘密 → 生成一次性链接 → 对方看完 → 没了。听起来很美。
但有个问题大多数人没想过:你的数据到底经历了什么?
就算号称"临时"服务,你的明文信息往往还是要经过他们的服务器。他们说不会看,说会删。但既然都是"承诺",为什么不直接把可能性消灭掉?
这就是端到端加密(E2EE)的价值所在。真正的 E2EE 下,服务器根本看不懂你的秘密。它存的只是密文——没有密钥的话,就是一堆无意义的随机字节。服务器就是个临时仓库,不是你的秘密管家。
端到端加密的秘密分享是怎么运作的
举个例子:Bob 想把数据库密码传给 Alice。
妙就妙在:加密全在 Bob 的浏览器里完成,在任何数据接触服务器之前就已经是密文了。
流程是这样的:
- Bob 输入秘密内容,选一个口令
- 浏览器用这个口令派生出加密密钥
- 秘密在本地用密钥加密
- Bob 把链接(包含密文和盐值)发给 Alice,口令通过另一条渠道传过去
为什么要分开发?因为如果链接和口令走同一条路,被人截了链接就全暴露了。链接走 Slack,口令走 Signal,或者短信,甚至物理纸条都行——关键是两条不同的攻击面。
Alice 打开链接,输入口令,浏览器重新派生密钥,本地解密。整个过程服务器从来没见过明文。
技术原理:其实没你想的那么复杂
这块稍微硬核一点,但我尽量说人话。
口令怎么变成密钥:PBKDF2
人天生不擅长生成随机、高熵的密钥。我们喜欢设"password123"然后觉得自己很安全。
所以需要一个函数,把弱口令转换成强密钥——故意慢的那种。
PBKDF2 就是干这个的。它把你的口令加上一段随机盐(盐是公开的),跑 60 万次 SHA-256 哈希。暴力破解的话,每猜一次都要耗 CPU 时间。60 万次迭代下来,破解一个普通口令变得不切实际。
async function deriveKey(passphrase, salt) {
const keyMaterial = await crypto.subtle.importKey(
"raw",
new TextEncoder().encode(passphrase),
"PBKDF2",
false,
["deriveKey"]
);
return crypto.subtle.deriveKey(
{
name: "PBDF2",
salt: salt,
iterations: 600_000,
hash: "SHA-256"
},
keyMaterial,
{ name: "AES-GCM", length: 256 },
false,
["encrypt", "decrypt"]
);
}
实际加密:AES-GCM
真正加密用的是 AES-256-GCM。不是你可能在教科书里见过的那种 AES——这是带认证的版本。
GCM 模式不仅加密数据,还生成一个认证标签。如果有人在传输途中改了密文,解密会直接报错,不会悄悄吐出乱码。
每次加密还需要一个唯一的初始化向量(IV)。可以理解为另一个"盐":公开、随机、但对安全至关重要。
AES-GCM 是硬货,美国国家安全局推荐用于机密信息的那种。
async function encrypt(plaintext, key) {
const iv = crypto.getRandomValues(new Uint8Array(12));
const encoded = new TextEncoder().encode(plaintext);
const ciphertext = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv: iv },
key,
encoded
);
return { iv, ciphertext };
}
零依赖的意义
这个方案挺激进的:不用 npm 包,不用加密库,不用信任第三方代码。
用的都是浏览器内置的 Web Crypto API。这套标准从 2015 年就开始推广了,现代浏览器全都支持。实现方是 Apple、Google、Microsoft、Mozilla 这些大厂,他们花了大功夫做正确、实现时间常数化的版本。
想想那些依赖一堆包的项目。Log4Shell 就是因为依赖出的事。left-pad 事件让半个互联网挂了也是因为依赖。每次 npm install 其实都是在做信任决策。
用 Web Crypto API,你信任的是浏览器原生实现。攻击面比信任某个每周下载量几百万的 npm 包小多了。
后端:傻存储就够了
后端只存这些:
- 密文(没有密钥就是废铁)
- 盐值(公开的,密钥派生需要)
- IV(公开的,解密需要)
- 元数据(创建时间、过期时间、查看次数)
Redis(或 Valkey)配置 save "" 和 appendonly no——完全不做持久化。进程一挂,数据全没。配置好 swap,防止任何数据落到磁盘。
服务器变成了一个临时的加密 blob 存储。它执行访问规则(一次性查看、过期删除),但就算想看内容也看不了。
能用在哪
直接嵌入到任何 web 应用里。加点表单、加几段 JS 函数,你的应用就有了安全的临时秘密分享功能。后端几乎不用改——服务器只管存取加密 blob。
这特别适合:
- 内部工具临时分享凭证
- 客服系统传输敏感数据
- 开发者分享 API 密钥或数据库密码
秘密在密码学上跟你的基础设施隔离了。就算有人拿下你的数据库,拿到的也只是没有口令就解不开的密文。
权衡利弊
PBKDF2 跑 60 万次迭代比 Argon2 慢?这是事实。Argon2 是目前密码哈希的金标准,但 Web Crypto API 暂时还不支持。
但想想使用场景:一次性秘密,通常几分钟内就被看了。计算成本完全能接受。
稍微弱一点的 KDF 换来零依赖——对大多数场景来说值了。你又不是在保护核弹发射密码,共享的数据库密码明天就改了。
自己做,不要盲信
端到端加密的好处就是:你不用信任服务提供方。
代码可以审计。可以自己部署。可以验证服务器真的从来没接触过明文。
在这个数据泄露天天上新闻的世界里,做出让服务器在密码学上"看不见"数据的系统,不只是炫技——是负责任的做法。
下次要在网上传敏感信息,记住:保护数据最好的方式就是让它在加密状态下离开用户的手。Web Crypto API 给你提供了工具,就在浏览器里,原生支持,即拿即用。