阅读约 7 分钟
CardanoWall 在浏览器里把你的密钥存在哪里
在浏览器中,CardanoWall 把已解锁的密钥保存在会话内存里,写入 IndexedDB 的只有加密后的保险库密文——绝不写入明文身份种子或私钥。

当你在浏览器中解锁 CardanoWall 时,解锁后的种子以及由它派生出的私钥都存在会话内存里——这是普通的应用程序内存,一旦你锁定、退出登录或关闭标签页,它就消失了。持久化的浏览器存储(IndexedDB、sessionStorage)只保存加密后的保险库密文和非机密的元数据。明文身份种子和派生私钥从不会写到那里。
这个区分正是关键所在。浏览器必须持有密钥材料,才能在本地签名和解密——这正是密码学得以运转的方式。安全问题从来不是「浏览器能不能碰到密钥?」它必须能碰到。真正的问题是这些密钥存在哪里,以及什么会被写入磁盘。CardanoWall 的 Web 模型就是为此设计的:在一次重新加载之后留存下来的是密文,而不是机密。
身份处于解锁状态时,浏览器需要什么?
它需要你当下手头工作所需的私钥材料——并且不会保留比会话本身更持久的任何东西。
在你解锁一个身份之后,应用可能需要:
- 对一条 Label 309 记录签名;
- 解密一条发给你的封存记录;
- 对收到的封存记录做试探性解密,找出真正发往你收件箱的那些;
- 在你添加或移除一个通行密钥后,重新加密保险库;
- 当你明确要求查看种子时,显示你的种子;
- 重建本地的加密缓存。
这些操作仅凭公钥都无法完成。每一项操作都需要从你的身份种子派生出的私钥材料。这套设计把该材料保存在会话内存中,并在锁定或退出登录时清除它——以尽力而为的方式,原因下文会讲到。
这里说的会话内存是什么?
会话内存是临时的、进程内的应用程序内存,解锁会话结束时应用就会丢弃它。
在 CardanoWall 的浏览器应用中,活动的种子以及由它派生的密钥被保存在普通 UI 状态之外的内部内存映射里。响应式的 UI 状态只保存非机密的事实:哪个身份处于解锁状态、它是什么时候解锁的、设置过程中屏幕上显示的是哪条通行密钥注册元数据。机密字节从不进入那份响应式状态。
这种隔离之所以重要,是因为普通 UI 状态恰恰是一个应用中最容易被检查、序列化、持久化,或被不慎传入某个会记录日志的组件的部分。机密材料理应拥有一个更小、刻意逐项列举出来的暴露面。应用中每一处能够交出机密字节的地方——签名闭包、解密密钥、显示种子的专用出口、保险库重建路径——都集中列在一个文件里,而且每一处返回的都是一份防御性拷贝,而不是活动缓冲区本身。
你处于解锁状态时,浏览器仍然持有这些机密。它们只是没有被当作普通的应用数据来对待。
什么会被写入 IndexedDB?
加密后的保险库密文——也就是服务器存储的那些相同字节,而不是其中包裹的种子。
IndexedDB 被用作你加密身份保险库的本地缓存:每个账户一行,保存着与服务器所存完全一致的、用 age 加密的密文。把它缓存下来,应用就能在页面重新加载后凭一次通行密钥轻触恢复运行状态,无需再次往返去取回保险库。
只读取这个本地数据库的攻击者,看到的是发给你通行密钥的加密保险库字节——而不是明文种子。这份缓存仍然是敏感的服务数据,应用会在退出登录、删除账户和通行密钥变更时清除它。但它不是身份本身;它是一个上了锁的盒子,而打开它的唯一钥匙只存在于你的认证器内部。(关于这个盒子是如何封存的,参见 CardanoWall 如何存储你的身份 以及 通行密钥如何保护你的身份保险库。)
在公用电脑模式下,以及当你选择不记住此设备时,这些写入会被完全禁止。
sessionStorage 里放的是什么?
只有非机密的注册元数据——绝不放密钥材料。
在创建身份的过程中,应用会把通行密钥的注册元数据镜像到 sessionStorage,这样设置进行到一半时若不慎重新加载,也不会清掉可见的状态。这份元数据在构造上就是非机密的:凭据 ID、公钥、传输方式、设备类型与备份标志,以及类似的、攻击者拿到也毫无所获的事实。
明确排除在 sessionStorage 之外的有:
- 身份种子;
- 派生私钥;
- WebAuthn PRF(伪随机函数)输出——通行密钥为解锁保险库而返回的那些机密;
- 保险库明文;
- 解密后的封存内容。
界线很简单。界面的连续性可以在重新加载后留存;身份机密则不应留存。
什么东西根本就不该出现在浏览器存储里?
明文身份材料——在任何存储中都不该出现。
下列内容被排除在 localStorage、sessionStorage、IndexedDB、cookie、日志以及普通应用状态之外:
- 身份种子;
- Ed25519 签名私钥;
- X25519 接收私钥;
- 混合后量子接收机密(可选的
age1pqc...地址背后的那个种子); - WebAuthn PRF 输出;
- 保险库明文;
- 解密后的封存内容,除非你明确地将它存入一个你能清晰掌控的本地加密缓存。
背后的道理与贯穿整套设计的那条一样:一个机密被写入的地方越多,要推断如何彻底删除它就越难,一旦被攻陷可读取的暴露面也越大。
公用电脑模式是什么?
它是明确的共享设备模式:开启期间,任何与身份相关的内容都不会被写入浏览器存储。
把它打开,应用就会跳过每一条与身份相关的持久化路径——没有 PIN 保险库、没有 IndexedDB 保险库缓存、没有版本锁定、没有 sessionStorage 注册镜像。解锁后的密钥只存在于会话内存中,能在标签页内的应用导航中留存,但会在关闭标签页或重新加载时消亡。开关本身刻意只存在于内存里:把「我正在用公用电脑」这件事持久化,本身就会成为一次浏览器存储写入;而一台共享机器应当始终以「再次询问」这个安全默认值重新打开。请在图书馆电脑、借来的笔记本、会议室机器、新闻编辑室的公用终端上使用它——任何不在你掌控之下的设备上都适用。
它不能做的,是让一台不可信的设备变得安全。如果在你输入种子或解锁身份时,机器已经被攻陷,它仍然能观察到内存中的机密。这个模式减少的是你离开之后留下的东西;它无法击败正在本地活动的恶意软件。公用电脑模式 一文介绍了完整的工作流程。
在 JavaScript 中,置零意味着什么?
它意味着把机密字节数组覆写掉——用零填满它们——并在应用用完它们后立即这样做。
CardanoWall 会在身份被锁定或你退出登录时,对它的 Uint8Array 密钥缓冲区做置零,而不是干等着、指望垃圾回收器去回收它们。这是良好的卫生习惯,值得去做。
但 JavaScript 并不是一个经过加固的机密处理环境,坦白说出这一点才是诚实的。字符串是不可变的,所以一个机密只要曾经以字符串形式存在过,就无法在原地抹掉。垃圾回收的时机不在应用的掌控之中。引擎可能在内部复制内存。开发者工具、扩展程序,以及一个被攻陷的源,会彻底改变风险模型。
所以这里的置零是一项尽力而为的措施,而不是一次有保证的擦除。它确实显著缩短了某份散落拷贝逗留的时间窗口;它并不承诺这些字节在每个角落都已消失。
如果页面在解锁状态下被攻陷会怎样?
那么身份就确实处于风险之中——这是任何基于浏览器的密码学中最难守住的边界。
一个拥有页面内容访问权的恶意浏览器扩展、一台被攻陷的设备,或一个在解锁会话期间运行的严重跨站脚本(XSS)漏洞,都可能从内存中读出机密,或者让应用去签名、解密一些你本不打算处理的东西。没有任何 Web 应用能完全消除这一点:经由网络抵达、随后又处理密钥的代码,会暴露给运行在同一个源中的任何其他东西。
CardanoWall 依靠纵深防御来收窄这个窗口:一份严格的内容安全策略,把脚本限制在应用自己的源内——不允许内联脚本、不允许 eval、完全不加载任何第三方脚本——外加在边缘为每个响应都盖上的安全响应头、一个刻意保持很小的机密暴露面、不做明文持久化,以及只在用户明确操作时才解锁和解密——绝不在标签页获得焦点时自动进行。这些措施降低了风险的概率与波及范围。但它们无法让一个运行在已解锁源内的恶意脚本变得无害;那仍然是这套浏览器模型所接受的、影响最大的威胁。
对于你最敏感的那些身份,正确的做法是把密钥材料移出共享的 Web 暴露面。把它们放在一台专用、可信的设备上,并优先采用一种机密根本不接触浏览器的工作流——在自动化中使用 CLI,或者 CardanoWall 桌面版,后者把你的密钥保存在本地的 Rust 核心里,而不是一个 Web 源中。(关于更宏观的原则,参见 为什么密钥从不离开设备。)
你实际上应该怎么做?
有意识地使用这套浏览器模型,并让防护措施与工作的敏感程度相匹配。
日常使用:
- 把你的 身份种子 存到一个安全的地方——它才是真正的备份;
- 添加一个通行密钥用于日常解锁;
- 用完后锁定或退出登录;
- 只在你信任的机器上使用「记住此设备」;
- 保持你的浏览器和操作系统处于最新版本;
- 避开高风险的浏览器扩展;
- 在任何共享设备上打开公用电脑模式。
对于敏感工作,再加上:
- 一个专用的浏览器配置文件,或者更好的,一台专用设备;
- 不在借来的机器上解锁;
- 用一把硬件安全密钥作为解锁要素;
- 把高风险身份与普通身份分开;
- 谨慎对待封存记录——一个能够解密的接收方,事后也能把明文泄露出去。
简短版
CardanoWall 的浏览器应用在身份处于解锁状态时需要私钥,因此它把私钥保存在会话内存里,而不是持久化存储中。IndexedDB 只缓存加密后的保险库密文;sessionStorage 只保存非机密的设置元数据。身份种子和私钥从不会作为普通浏览器数据被写入。
托管保险库模型与浏览器存储模型相互加固:服务器持有的是它自己也无法解密的密文,而浏览器则尽力不把种子留在身后。在一台已经被攻陷的设备面前,这两项都不构成保证——而把这个限度讲清楚,正是认真对待它的一部分。要全面了解这项服务能看到与看不到什么,参见 CardanoWall 能看到什么。