蛙记安全白皮书
基于当前客户端实现,说明蛙记的密钥层级、加密同步、设备保护和凭据提供安全模型。
本文说明蛙记当前客户端与浏览器扩展的安全模型、信任边界和已实现的保护措施,重点解释密钥如何产生和使用、哪些数据在同步前加密,以及同步服务仍需处理哪些运行元数据。
本文不是对任何环境的绝对安全承诺,也不替代独立安全审计。产品、平台或协议发生实质变化时,本文应随之更新。
1. 范围与安全主张
蛙记采用客户端加密模型:密码库中的敏感字段在客户端或浏览器扩展中加密和解密;同步服务无需获得这些字段的明文即可保存、下发和同步它们。成功登录本身不等于获得明文密码库,客户端还必须使用主密码派生的密钥解开用户密钥和密码库内容。
这个主张有明确边界。同步服务并非只处理密文,它还需要保存和处理账户、项目、文件夹、组织或集合之间的关联,以及授权、同步、排序、删除和分享所需的运行元数据。本文将“加密内容”和“服务可见元数据”分开说明,不把两者混为一谈。
2. 系统架构与信任边界
安全模型由四个边界组成:
- 客户端、浏览器扩展与主密码。 主密码在客户端或扩展中用于派生主密钥、解密账户密钥并打开密码库。登录请求使用由密码和主密钥导出的验证值,而不是把主密码作为登录字段直接发送。
- 同步服务。 服务负责身份认证、授权、保存同步对象和运行同步流程。它接收已加密的密码库字段,同时处理同步所需的标识符、时间戳和权限等元数据。
- 设备与平台凭据接口。 本地数据库、系统密钥库、生物识别、自动填充和凭据提供扩展都处于设备及其操作系统安全边界内。已解锁的设备、被授予自动填充内容的目标应用,以及具有系统级控制权的攻击者,不在“同步服务看不到明文”这一主张的保护范围内。
- 网页与扩展运行环境。 浏览器扩展的后台和扩展页面属于受信任的扩展上下文;内容脚本只在这些上下文与网页之间传递受限操作。网页本身不属于受信任的密码库上下文,不能直接调用扩展后台的敏感接口。
3. 数据分类
加密的密码库内容
下列内容在写入同步对象前会经客户端密码学上下文转换为加密字符串:
| 内容类别 | 代表性字段 |
|---|---|
| 通用项目内容 | 项目名称、备注、标签、自定义字段名称和值 |
| 登录凭据 | 用户名、密码、密码历史、网站或应用 URI、URI 校验值 |
| 二次验证 | TOTP 密钥 |
| 通行密钥 | 凭据 ID、密钥类型/算法/曲线、私钥值、依赖方与用户元数据、计数器 |
| 其他敏感项目 | 银行卡、身份资料、SSH 密钥中的敏感字段 |
| 附件相关元数据 | 附件文件名和附件密钥材料 |
每个项目可拥有独立的项目密钥材料;项目密钥本身再由更高层的账户或组织密码学上下文加密。也就是说,当前实现是字段和对象级的加密同步,并不是把整个密码库封装为一个不透明的上传文件。
同步服务可见的运行元数据
为使同步和授权可用,以下类型的信息会以可处理的形式出现。具体字段随功能而变,但不属于上表所列的密码库内容明文:
| 用途 | 代表性元数据 |
|---|---|
| 账户与授权 | 账户 ID、邮箱、组织/集合/文件夹 ID、成员或项目权限、订阅与配置状态 |
| 同步与生命周期 | 项目类型、创建/修订/删除时间、远端 ID、删除状态、文件夹与集合关联、收藏状态 |
| 安全分享的运行控制 | 分享访问 ID、类型、到期和删除时间、当前/最大访问次数、禁用状态、隐藏邮箱选项 |
因此,服务可能了解密码库中有多少项目、项目的类型和变更时间等运行信息;本文不声称它对这些元数据一无所知。
4. 密钥层级与主密码派生
客户端先通过预登录配置取得账户的密钥派生参数,然后使用主密码和账户邮箱相关的盐派生 32 字节主密钥。系统支持 PBKDF2 或 Argon2id 两种模式:PBKDF2 使用规范化小写邮箱作为盐和账户配置的迭代次数;Argon2id 使用配置的迭代次数、内存和并行度。当前注册账户默认使用 PBKDF2,迭代次数为 600,000;
主密钥再经 HKDF 以不同信息标签派生独立的加密密钥和 MAC 密钥。注册时,客户端生成随机的 64 字节用户对称密钥,并用主密码派生的密钥加密后保存。支持的流程还可生成 RSA-2048 密钥对:公钥作为公开材料保存,私钥由用户对称密钥加密。
这形成如下层级:主密码用于派生主密钥;主密钥用于保护用户对称密钥;用户或组织密码学上下文用于保护项目密钥及其内容。主密码的强度仍然决定离线猜测攻击的成本,应使用唯一且足够长的主密码。
5. 密码库加密与完整性校验
当前同步写入采用 AES-256-CBC 加密,并使用 HMAC-SHA-256 校验完整性。密文包含内部格式标识,以便客户端在格式演进时选择兼容的解密和完整性校验规则。
| 项目 | 当前实现 |
|---|---|
| 加密 | AES-256-CBC,PKCS#7 填充 |
| 完整性 | 对 IV 与密文计算 HMAC-SHA-256 |
| 密钥材料 | 64 字节,前 32 字节为 AES 密钥,后 32 字节为 MAC 密钥 |
| 随机初始化向量(IV) | 每次加密时随机生成 16 字节,用于避免相同内容产生相同密文 |
客户端在解密前先验证 MAC;验证失败会拒绝解密。这是“先加密、后认证”的组合,而不是把 CBC 模式误称为带认证加密模式。客户端保留对较早密文格式的读取兼容性。
6. 登录、新设备与会话边界
登录前,客户端根据账户配置在本地完成主密钥派生,并生成登录所需的派生验证值。原始主密码不作为该登录请求的密码字段发送。认证完成后,客户端下载加密的账户资料和密码库对象;只有成功解开受主密钥保护的用户密钥后,才能解密对应内容。
访问令牌和刷新令牌用于后续已认证请求。客户端还会处理账户安全状态变化和会话失效;当受保护的账户材料无法按当前会话解开时,会将其视为需要重新建立会话的情况。新设备或清除本地状态后,仍需要能导出正确主密钥的主密码来恢复密码库访问。
7. 加密同步、一致性与删除
同步流程在客户端建立解密上下文:下载后在本地解密对象,上传前在本地重新加密变更。服务据此保存加密字段和前述运行元数据,而不需要为同步读取项目名称、密码或 TOTP 密钥等字段的明文。
项目使用远端标识符、修订时间和删除状态参与同步。客户端会按项目的合并规则处理本地与远端版本;可合并的内容会合并,其他冲突按修订状态处理。删除也通过同步状态传播。此机制追求多设备的一致性,但不应被解读为对恶意或不可用服务端的可用性、完整备份或回滚防护承诺。用户应保留经加密保护的备份,并在重要变更后确认同步结果。
8. 本地存储、锁定与生物识别
主会话创建后,各平台的本地密码库数据库均以会话主密钥加密打开。Android 与 iOS 使用 SQLCipher。Android 数据库位于不参与备份的应用目录;iOS 在打开数据库时会检查 SQLCipher 是否实际启用。设备丢失时,这层保护与操作系统的应用隔离共同降低静态数据库文件直接暴露的风险,但不保护已经解锁、被恶意软件控制或被系统级攻击者接管的设备。
生物识别是本地解锁便利功能,不是远程身份认证,也不取代主密码:
- Android: 使用 Android Keystore 中要求
BIOMETRIC_STRONG用户认证的 AES 密钥,加密本机保存的解锁材料。 - iOS: 使用 Keychain 的“仅本设备、设备解锁时可用”项,并绑定当前生物识别录入集。录入集变化会使该项不再可用,需要重新配置。
生物特征数据由操作系统处理,客户端不会将指纹或面容数据发送给同步服务。
9. 自动填充、TOTP 与通行密钥
自动填充会根据保存的 URI 与当前网站或应用目标匹配候选项目。匹配支持域名、主机、前缀、精确匹配、正则表达式和“永不匹配”,并可考虑等价域名以及 Android/iOS 应用 URI。密码库锁定时,Android 自动填充服务只提供进入解锁流程的入口,不直接提供凭据。URI 匹配是减少错误建议的辅助机制,用户仍应核对网站、应用和账号;它不是对钓鱼或恶意应用的绝对防护。
浏览器扩展会把自动填充限定为扩展发起的受控流程。网页不能直接向扩展后台索取凭据;内容脚本只能请求其被授予的操作,后台会按发送方上下文限制消息能力。填充命令在执行前会再次校验当前文档、URL、来源和 frame 是否仍与候选项生成时一致,并设置短时有效期。沙盒 iframe 会被拒绝填充;HTTPS 凭据填入 HTTP 页面或跨域 iframe 时,扩展会要求用户确认。扩展显示的内联界面运行在扩展页面 iframe 中,消息同时校验来源、扩展 origin 和会话 nonce,以降低网页伪造扩展界面的风险。
TOTP 密钥作为登录项目中的加密字段保存。解锁密码库后,客户端才能读取该密钥并参与相应的验证流程。
通行密钥使用平台凭据提供接口。Android 在创建和使用请求中校验依赖方 ID 与调用来源的关系,将挑战和来源写入客户端数据,并使用 P-256 / ES256 进行密钥和签名处理。iOS 凭据提供扩展处理依赖方标识、客户端数据哈希和用户验证要求,再完成相应断言或注册响应。通行密钥的私钥和相关元数据依旧是密码库中的加密字段;当请求要求用户验证时,流程会要求完成相应验证后才继续。依赖方服务器仍负责验证认证响应并执行其自身的账户安全策略。
10. 浏览器扩展运行环境与会话边界
浏览器扩展复用前述的主密码派生、受保护用户密钥、项目密钥和加密同步模型。解锁后,用户密钥和项目密钥由 Rust/WASM 密钥库持有;扩展的常规 TypeScript 会话只持有不透明句柄和密钥槽引用。密码和 TOTP 密钥不会进入长期的凭据列表,只有在填充、显示、复制或生成验证码等明确动作需要时才按条目解密。
扩展不使用 SQLCipher 数据库。它可在浏览器扩展存储中缓存仍为端到端密文的同步对象,并保存账户、KDF 配置、受保护用户密钥、令牌和超时等运行材料。可用时,扩展会将本地存储访问限制为受信任的扩展上下文。浏览器配置目录或扩展自身被攻破时,这种存储保护不等同于原生应用的数据库加密;缓存密文仍需要正确的密钥材料才能解密,而令牌等运行材料应被视为需要保护的账户访问材料。
为应对 Manifest V3 后台工作线程被回收,扩展可以在浏览器会话存储中保留短生命周期的会话恢复包装密钥,并以 AES-GCM 密封的形式保存恢复载荷。该机制用于当前浏览器会话内的后台恢复,不是永久的无主密码解锁机制;不允许恢复时,相关材料会被清除。
当已配置本地解锁时,扩展通过 Native Messaging 与本机已安装的解锁组件建立临时加密通道。系统级生物识别验证和受保护密钥材料由该本机组件及操作系统负责;扩展仅在单次恢复流程中处理短生命周期的用户密钥副本,并在使用后清零。
11. 安全分享模型
已实现的文本分享使用独立的随机 16 字节分享密钥材料,并通过 HKDF 扩展为 64 字节的加密/MAC 密钥。分享标题、备注和文本内容在同步前使用分享密码学上下文加密;分享密钥在拥有者的同步资料中由账户密码学上下文保护。
可选的分享密码不会作为原始密码上传:客户端以分享密钥为盐,使用 PBKDF2 进行 100,000 次迭代生成验证哈希。与此同时,到期时间、删除时间、访问次数限制、当前访问次数、禁用状态等控制信息属于服务端可处理的分享元数据。
12. 威胁模型、非目标与用户责任
本模型旨在降低同步服务直接读取已加密密码库字段的风险,并通过独立密钥、完整性校验、本地数据库加密、浏览器扩展隔离和平台解锁能力降低常见的存储与传输后暴露风险。它不承诺防御下列情况:
- 攻击者获得已解锁设备、已解锁密码库、主密码,或能在客户端进程中读取明文;
- 恶意或被攻破的同步服务造成拒绝服务、删除数据、隐藏变更、篡改可见元数据,或重放旧的有效同步对象;
- 用户向错误的网站、应用或他人填入或分享凭据;
- 用户确认浏览器自动填充后,目标网页脚本读取已写入其表单的凭据;
- 浏览器配置目录、浏览器进程、扩展安装包或本机解锁组件被恶意读取、替换或控制;
- 依赖方服务自身的认证缺陷、账户恢复流程或其服务端泄露;
- 设备操作系统、硬件、第三方输入法、浏览器或凭据接口本身的漏洞。
用户应使用唯一的强主密码,及时安装系统和应用更新,谨慎配置 URI 匹配和分享有效期,并保存可恢复的加密备份。主密码遗失或已解锁设备被控制,不能由本文所述的同步加密机制自动补救。
13. 维护与变更
本文记录的是当前实现,而不是永不变化的协议冻结版。下列变化应触发安全复核和白皮书更新:密钥派生参数或默认值、密文格式、密钥层级、同步字段分类、本地数据库保护、浏览器扩展权限与消息边界、WASM 密钥处理、会话恢复、本机解锁通道、平台凭据接口、分享流程,以及任何影响服务端可见信息的变更。