安全

金库是如何建造的,以及一旦被攻破会发现什么。

最后更新日期:2026 年 9 月 21 日 · AssuredAuth,由 CircleRoll LLC 提供

这是 AssuredAuth 如何保护您存储的双因素密钥、密码和银行卡的公开说明。编写方式旨在方便注重安全的读者对照应用程序和已发布的测试向量(任何审阅者均可通过 hello@assuredauth.com 索取)验证每一项声明。本说明仅陈述事实,不作任何夸大其词:这里没有任何内容声称绝对安全,也没有任何百分比的承诺。

简而言之:保险库存在于一个地方,那就是你的手机。其中的每个值都使用手机安全硬件保存且无法导出的密钥进行加密。我们不运行任何保险库服务器,不保存任何副本,也不持有任何密钥。备份文件只有在使用绝不会存储在任何地方的密码短语加密后才会离开手机。

报告问题: hello@assuredauth.com. 请参阅文末的“报告缺陷”。

1. 加密的内容是什么?使用什么加密?

数据在手机上密钥
双因素密钥(TOTP/HOTP/Steam 种子) 在手机上钥匙串(iOS)/ Android Keystore 支持的商店 密钥平台的硬件绑定密钥;数据库行仅包含引用(secretRef),绝不包含种子。
密码(密码值、备注) 在手机上密封行:AES-256-GCM,每个值使用一个随机的 96 位 nonce 值 密钥一个随机的 256 位数据密钥,其本身由第 2 节中的硬件密钥封装。
卡片信息(卡号、有效期、持卡人姓名、CVC码) 在手机上与密码相同的安全印章;CVC 是可选的,只有在您输入后才会存储。 密钥相同的数据密钥
元数据(服务名称、用户名、图标、排序方式) 在手机上该应用程序的数据库(SwiftData / Room) 密钥受操作系统文件加密保护;被视为非机密信息
备份文件(.authbackup) 在手机上无论你把它放在哪里:文件、iCloud 云盘、Google 云端硬盘 appData 文件夹、共享表单 密钥密钥由您的口令生成(§3)。苹果和谷歌只会收到密封文件,不会收到其他任何信息。
Watch / Wear 配套应用 在手机上手表自带的钥匙串/加密共享偏好设置 密钥手表平台的密钥;通过平台的配对设备通道传输
桌面扩展流量(一次性代码按需提供) 在手机上不存储;以端到端加密信封的形式传输 密钥每个密钥对对应一个 X25519 密钥对,私钥位于密钥链/密钥库中;中继节点只能看到密文。

诊断功能没有接受密钥的方法。任何分析事件都不会包含密钥库字段。在任何版本中,密钥库中的任何值都不会写入日志。

2. 各个平台上密钥的存放位置

iOS。 数据密钥由安全隔离区 (kSecAttrTokenIDSecureEnclave) 内生成的密钥封装,该密钥具有访问控制权限 .privateKeyUsage + .userPresence、辅助功能 …ThisDeviceOnly,并位于应用程序共享的钥匙串访问组中,以便自动填充扩展程序可以使用相同的密钥。应用程序、备份或调试器都无法从隔离区读取私钥;隔离区仅在用户在线时(通过面容 ID、触控 ID 或设备密码)才会同意使用该私钥。ThisDeviceOnly 表示手机的 iCloud 或 iTunes 备份不包含该密钥;恢复的手机初始状态为空,需要输入备份文件和密码。没有密码的手机无法设置 .userPresence 密钥(Enclave 拒绝创建该密钥),模拟器也遵循同样的设计(其存在提示是一个任何测试都无法回答的警报):在那里,数据密钥只是一个普通的仅限设备使用的钥匙串项目——仍然不在任何备份中,手机锁定时仍然无法读取,但没有第二个提示——“安全”屏幕也正是如此显示,并建议设置密码。

安卓。 数据密钥由 AndroidKeyStore 的 AES-256 密钥封装,该密钥在设备拥有 StrongBox 时生成(否则使用 TEE),并启用了 setUserAuthenticationRequired(true) 和 30 秒的身份验证有效期(在进行生物识别或设备凭证验证后),以及 setUnlockedDeviceRequired(true) 和 setInvalidatedByBiometricEnrollment(true)。最后一个标志仅用于验证,而非承担负载:Android 仅将其应用于每次使用都需要生物识别的密钥,因此对于允许使用设备凭证的 30 秒有效期密钥,它不会产生任何影响——防止陌生人访问的原因是,注册密钥需要 PIN 码,并且移除屏幕锁定会使密钥永久失效。密钥库密钥不可导出,并且不包含在 Android 的云备份中。应用程序在其自身视图中指定的域名(webDomain)仅在已知浏览器中有效;对于任何其他软件包,它还必须是系统已验证过的该软件包的域名(数字资产链接),因为任何应用程序都可以声明任何域名。

这对被盗的数据库文件意味着什么? SQLite 文件和密封行都是密文。用于打开它们的密钥存储在文件来源手机的安全硬件中,无法随文件一起复制。离线状态下无法猜测密钥:数据密钥是随机生成的,并非人为设定。

3. 备份文件

备份是唯一会离开手机的数据,因此理论上来说,它是唯一可能遭受离线攻击的地方。这种设计使得此类攻击成本高昂,并尽可能地限制了攻击者可以利用的资源:

该文件为 JSON 格式,任何文本编辑器均可读取;只有密文部分是不透明的。五年后,如果有人在应用程序已失效的情况下找到该文件,他们可以清楚地看到其加密方式,并使用 OpenSSL 或任何 Argon2 库打开它。

格式 v1(随 1.0 版本发布,仍然可读)

{
  "format": "authbackup",
  "version": 1,
  "kdf": "PBKDF2-HMAC-SHA256",
  "rounds": 600000,
  "salt": "<base64, 16 bytes>",
  "cipher": "AES-256-GCM",
  "nonce": "<base64, 12 bytes>",
  "ciphertext": "<base64>",
  "tag": "<base64, 16 bytes>"
}
步骤字节
keyPBKDF2-HMAC-SHA256(passphrase as UTF-8, salt, 600000 rounds, 32 bytes)
aadUTF-8("authbackup:1:600000:") ‖ salt (原始的 salt 字节,而非 base64 编码)
ciphertext, tagAES-256-GCM(key, nonce, plaintext, aad); 标签 16 字节,单独存储
plaintextUTF-8 JSON,键已排序: {"accounts":[…],"exportedAt":"…Z","passwords":[…],"version":1}

兼容性说明:Android 1.0 写入的是 v1 的一个变体——kdf 文件全部小写,没有标签密钥(附加到密文的 16 字节标签),也没有其他数据。两个平台仍然会读取这些文件(当缺少标签时:分割最后 16 个字节,使用空的 AAD 打开;kdf 文件大小写不区分地进行比较),并且现在都只写入上述布局。

格式 v2(当前版本;新备份的默认格式)

{
  "format": "authbackup",
  "version": 2,
  "kdf": "Argon2id",
  "memoryKiB": 65536,
  "passes": 3,
  "lanes": 1,
  "salt": "<base64, 16 bytes>",
  "cipher": "AES-256-GCM",
  "nonce": "<base64, 12 bytes>",
  "ciphertext": "<base64>",
  "tag": "<base64, 16 bytes>"
}
步骤字节
keyArgon2id(version 0x13, passphrase as UTF-8, salt, m = 65536 KiB, t = 3, p = 1, tagLength 32, no secret, no associated data)
aadUTF-8("authbackup:2:argon2id:65536:3:1:") ‖ salt
ciphertext, tagAES-256-GCM(key, nonce, plaintext, aad); 标签 16 字节,单独存储
plaintextUTF-8 JSON,键已排序: {"accounts":[…],"cards":[…],"exportedAt":"…Z","passwords":[…],"version":2}

Argon2id 是参考 C 实现(phc-winner-argon2,CC0),已在两个平台上提供。在目前的手机上,64 MiB 的数据和 3 次迭代大约需要一秒钟,并且每次猜测都会消耗相同的内存和时间——这是 PBKDF2 在 GPU 平台上所不具备的特性,也是 v2 版本存在的原因。读卡器会忽略它不认识的密钥,因此,如果使用 v2 版本的读卡器,v1 版本根本无法识别卡。

证明

三个测试向量文件(backup v1、backup v2 和 Argon2id)由第三方实现生成,该实现独立于上述两个应用程序(Node 的 OpenSSL 用于 PBKDF2 和 AES-GCM,以及在生成时根据参考实现的已发布答案进行检查的 WebAssembly Argon2id)。每个文件都包含密码短语、盐值、随机数、明文、派生密钥、AAD、预期密文和标签,以及完整的文件。iOS 和 Android 上的单元测试读取相同的文件并断言相同的字节——因此,在一个平台上密封的备份文件已被证明(而非假定)可以在另一个平台上打开,并且三个平台的测试结果一致并非同一错误的两个副本。配套工具会打开手机实际使用第三种实现方式写入的文件,并根据上述规则检查标头(格式的键必须完全一致,因此不能包含提示或名称;16 字节的盐值、12 字节的 nonce 值和 16 字节的标签分别存储;版本 2 的大小正好是 64 MiB / 3 / 1;错误的密码短语会被拒绝);自动化的 iOS 运行程序会在模拟器上写入一个文件并以这种方式打开它,并在模拟器的应用程序容器、应用程序组容器和钥匙串数据库中搜索每个种子密码和卡号的 UTF-8 和 UTF-16 编码(0 个结果)。

4. 威胁模型

情况他们拥有什么他们能得到什么
手机丢失或被盗,已锁定 他们拥有什么设备、数据库文件、密封行 他们能得到什么密文。封装密钥位于安全隔离区/保险箱中,只有在设备锁定并输入新的生物识别信息或密码后才能释放。远程擦除手机数据(例如使用“查找我的设备”功能)会同时销毁密钥材料。
被盗的备份文件 他们拥有什么来自云帐户、电子邮件或笔记本电脑的 .authbackup 文件 他们能得到什么使用由至少 12 个字符的密码短语派生的密钥,并为每个文件设置盐值,采用 Argon2id 算法,文件大小为 64 MiB × 3 次(v2 版本),或 PBKDF2 算法,迭代次数为 600,000 次(v1 版本)。不提供任何提示、用户名或明文账户列表。每次猜测都需要重新进行密钥派生;没有捷径可走。强密码短语是这里唯一的防御手段,应用程序的说明文档中也明确指出了这一点。
被盗设备,屏幕已解锁 他们拥有什么你手里拿着一部未解锁的手机 他们能得到什么代码列表。保险库(密码、银行卡)位于应用锁之后,并会根据自动锁定定时器(立即/1分钟/5分钟)重新锁定;解密后的值仅在屏幕显示时才会存在于内存中。即使应用处于打开状态,自动填充和纯文本导出功能也会要求重新进行生物识别。如果您担心这种情况,请将应用锁设置为“立即”。
同一部手机上的恶意应用程序 他们拥有什么另一个应用程序的沙盒、剪贴板、辅助功能或叠加技巧 他们能得到什么它无法读取我们的文件(平台沙盒机制)或使用我们的密钥(密钥与我们的签名身份和用户在线状态绑定)。复制的内容会在 30 秒后从剪贴板清除;在 Android 系统上,复制的内容会被标记为敏感信息,并从剪贴板历史记录中移除。在 Android 系统上,保险库屏幕会设置为 FLAG_SECURE 状态(禁止截图、屏幕录制,切换器中显示空白);在 iOS 系统上,切换器缩略图也会显示空白。Android 系统的自动填充功能仅提供给系统已验证过与已保存域名匹配的应用(数字资产链接),因此不会向外观相似的应用提供您的密码。
钓鱼页面 他们拥有什么一个看起来像你保存的网站。 他们能得到什么没有任何内容会被自动填充。匹配依据是可注册域名(公共后缀列表,已随应用捆绑并更新):login.example.com 和 example.com 匹配,example.com.evil.net 和 examp1e.com 不匹配。应用内浏览器匹配方式相同,并且只会填充到请求的框架中,并在填充时再次检查该框架的来源;对于没有可注册域名的主机(例如 IP 地址或本地网络上的裸域名),只会进行精确匹配,不会有任何偏差。iOS 自动填充功能还增加了 Apple 的关联域名检查。需要手动粘贴到类似域名的密码仍然存在风险;应用无法消除这种风险,只能避免提供辅助。
我们自己的服务器 他们拥有什么— 他们能得到什么没有保险库服务器。AssuredAuth 没有账户,无需登录,也没有您的保险库副本,因此我们这边不会有任何您的信息被窃取或破解。服务器端运行着两项服务,但这两项服务都不会触及您的保险库:一是桌面扩展程序的中继邮箱,用于存储端到端加密的信封(一次性代码,读取后删除;中继邮箱永远不会持有密钥);二是可选的私有连接的出口服务器,用于传输流量,但不保留任何访问日志。
传票或政府请求 他们拥有什么针对我们的法律程序 他们能得到什么我们没有任何可以交出的:我们没有保险库,没有钥匙,没有密码,也没有备份。我们可以被迫这么说;但我们无法被迫提供不存在的东西。

5. 剪贴板、屏幕截图和内存

6. 自动填充:哪些内容会填充,哪些内容不会填充

在哪里密码卡片
iOS:Safari 和应用程序(自动填充扩展) 密码是的,在扩展程序 (LAContext) 中重新输入 Face ID/Touch ID/密码后,并且仅针对保存条目的可注册域名,并结合 Apple 的应用关联域名检查。 卡片不。Safari 中的卡片自动填充功能是苹果独有的;卡片屏幕上显示“在 AssuredAuth 浏览器中填充并使用 Copy 功能”,除此之外没有其他承诺。
Android:应用和 Chrome(自动填充服务) 密码是的,基于数据集的身份验证:系统会在填写数据前显示生物识别提示;只有当数字资产链接将应用包与已保存的域关联时,应用包才会匹配。 卡片是的,信用卡号、信用卡有效期*和信用卡安全码提示,都属于同一个生物识别门禁系统。
AssuredAuth 浏览器(两个平台) 密码是的,通过可注册域名——一个自身没有任何完全匹配项的主机——进入所要求的框架,在填充之前进行生物识别。 卡片是的,通过现场检测,在填充前进行生物识别。
复制按钮 密码应用锁后面是 30 秒剪贴板 卡片号码、有效期、CVC(规则相同)

在外形相似的宿主身上,什么都填不进去。没有生物识别信息,什么都填不进去。

7. 永远不会发生的事情

没有保险库服务器。没有包含保险库字段的分析工具。没有确认表和新的生物识别信息,无法导出纯文本。保险库屏幕上不加载第三方 SDK。不提供保险库屏幕截图。不提供密码提示。任何版本均不记录数值。

8. 我们无法防范的

一个弱到容易被猜到的密码短语。一部操作系统根源已被攻破的手机(越狱或root后的设备可以向应用程序谎报上述所有信息)。打字时被人监视。粘贴内容时桌面上的恶意软件。我们这样说,是为了让大家明白以上列表的真正含义:设计可以移除的那些东西。

9. 审核状态

该设计已针对两个平台上已发布的已知答案以及其自身的跨平台向量进行了单元测试。在将该存储库上架商店之前,所有者计划进行外部审查;在此之前,本文档将在此处进行说明。

10. 各部分在哪里运行

保险库中的数据不会离开手机,但其中一些组件会在其他人的电脑上运行。以下是组件列表及其各自包含的内容;数据流的具体情况请参阅隐私政策:https://assuredauth.com/privacy。

每款产品都包含其对应的产品信息,不包含任何来自其他渠道的内容。产品更新时,产品列表也会随之更新,本部分内容也会相应更新。

报告安全漏洞

如果您认为发现了本文所述任何方面的安全漏洞,请发送邮件至 hello@assuredauth.com,邮件主题为“安全”。请在邮件中注明平台、应用版本和操作步骤;使用您自己的数据进行测试即可,请勿使用他人的数据进行测试。我们不提供 PGP 密钥;如果报告不适合以纯文本邮件形式发送,请在第一封邮件中说明,我们会安排其他方式。

作为回报,我们承诺:我们会在三个工作日内确认收到您的信息,并在处理过程中随时向您汇报进展,在发布任何内容之前修正错误,如果您希望署名,我们会在此处注明您的姓名。对于在以下范围内出于善意进行的研究,我们不会采取法律行动。

适用范围:iOS 和 Android 应用、Apple Watch 应用、桌面扩展程序、assuredauth.com 及其 API。不适用范围:App Store 和 Google Play 本身、上述服务提供商、任何形式的社会工程攻击以及拒绝服务攻击。

目前没有赏金。对于举报,我们会修复问题并表示感谢,而不是支付金钱。