团队 Box 审计器 (Team Box Auditor)
背景
由团队签名链提供的可审计且可加密证明的团队成员资格,是一项重要的安全要求,可以防止服务器将“幽灵用户”注入 Keybase 团队。
每当团队管理员添加 or 移除用户时,此声明都会被签名到签名链中,所有团队成员都可以看到它,并且他们的客户端会确保该声明是由团队管理员签名的。此外,当团队成员被移除时,管理员会轮换团队的密钥,这样被移除的团队成员就无法访问用于解密和签名新消息和文件的密钥。
然而,在某些情况下,我们要根据某种行为来轮换团队的密钥,但执行该行为的用户无法自己轮换团队。
- 团队成员离开团队
- 隐式管理员离开他们作为成员的唯一父团队
- 团队成员(或隐式管理员)撤销设备(这会轮换用户的每用户密钥 (PUK))
- 团队成员(或隐式管理员)重置其账户
- 团队成员(或隐式管理员)撤销设备(这会轮换用户的每用户密钥 (PUK))
例如,如果读者决定离开团队,管理员必须稍后回来并在事后轮换团队的密钥。同样,正在重置账户的用户可能没有轮换团队所需的密钥,因此团队稍后需要由管理员轮换。
在这些情况下,级联惰性密钥轮换 (CLKR) 机制让服务器向团队管理员发出信号,告知有人离开或有了新密钥,因此管理员可以正确地轮换团队的密钥,并为正确的用户及其当前密钥加密它们。
问题
然而,CLKR 完全由服务器驱动。虽然服务器在加密学上不可能欺骗客户端添加任意成员,但服务器可以(例如)忽略告诉团队管理员有关团队成员设备撤销的信息。
想象一下,一位 Keybase 用户的笔记本电脑被攻击者窃取了。即使用户从另一台设备上撤销了那台笔记本电脑的密钥,用户所在的所有团队仍然拥有为旧用户密钥加密的团队密钥。如果恶意服务器未能发布 CLKR,并且还与攻击者合作提供加密的团队数据,攻击者就可以读取新的团队消息和文件。
解决方案
因此,我们引入了客户端 Box 审计器,它会审计用户所在的每个团队的团队加密 (Teams Crypto)。请注意,一对一和群组聊天在幕后也由团队机制驱动,因此这些聊天也会被审计。
Box 审计器是一个后台进程,它随机选择用户所在的团队,加载其所有成员的签名链,并验证团队的 box 当前是否为正确的用户密钥加密。如果不是,审计器会轮换团队的密钥。通过 Keybase Merkle 树的安全性,服务器无法撒谎、回滚或模棱两可地陈述用户的用户密钥(或团队的成员资格),因为它已签名到他们的签名链中。
请注意,Box 审计器的设计使得它不可能被试图阻止这些审计的恶意服务器“欺骗”。审计期间来自服务器的任何可能的错误,包括网络错误和身份验证错误,都不受客户端信任,客户端会安排在不久的将来再次进行审计。如果单个团队审计失败的次数足够多,该团队将被监禁(jailed),并且只要访问该团队,用户就会收到通知,直到它通过审计。这样,恶意服务器就无法通过假装客户端的请求格式不正确或假装宕机来停止审计。
试用一下
您不需要做任何特别的事情来让审计器保护您的团队:后台进程会自动完成所有工作。
但是,如果您想手动运行审计,Keybase 客户端 4.0.0 附带了 Box 审计器的命令行界面。
审计 keybase 团队:
keybase audit box --audit --team keybase
审计本地缓存中的所有已知团队,包括一对一聊天和群组聊天:
keybase audit box --audit-all-known-teams
请注意,如果您在子团队中但不在该子团队的父团队中,此命令将列出该父团队的审计为失败。这是有意为之的;有关详细信息,请参阅下一节。
实现细节
我们需要的一项关键功能是在给定的 Merkle 根(称之为 box 摘要)处获取所有团队成员的用户密钥。
函数 calculateSummaryAtMerkleSeqno 的工作原理如下:
- 加载团队的签名链
- 构建 Merkle 检查点映射。 如果我们正在获取历史摘要,对于每个活跃成员,检查点是他们上次被给予 box 时的 Merkle 根:在上次团队轮换时,或者当他们被添加到团队时(如果自那时以来未发生轮换)。 如果我们正在获取当前摘要,对于每个活跃成员,检查点是可用的最新 Merkle 根。
- 在 Merkle 检查点遍历全局 Merkle 树,以查找团队密钥为用户加密时用户签名链的最后一个链接
- 对于每个用户,根据用户的签名链和上述信息推断用户密钥生成(generation)。
- 构建一个 box 摘要,这是一个从用户 ID 和最老 seqno(即 1 或他们上次重置账户时的签名链链接号)到他们的用户密钥生成的映射。
一旦我们有了这个原语,我们就可以获取上次轮换的 Merkle 根处的摘要,并将其与当前 Merkle 根处的摘要进行比较。如果这些不同,则轮换并稍后重试。如果服务器出现任何其他错误,也执行相同的操作。
如果团队审计失败超过 6 次,该团队将进入 box 审计监狱。
被监禁时,任何通过常规团队加载器或快速团队加载器加载团队的尝试都会导致重新审计。如果该重新审计失败,客户端会反复警告用户审计已失败。一旦我们要确信审计器按预期工作,我们可能会更新客户端以开始完全拒绝加载团队。
审计和重试在 Keybase 服务运行时作为自动后台任务处理。
请注意,Box 审计器目前处于服务器控制的功能标志之下,因为它正在测试中,我们要确保一切正常工作。在下一个版本中,此标志将被移除,以便服务器无法单方面关闭审计器。
客户端必须注意一些细节。
- 隐式管理员不在团队中,但是是父团队的管理员,他们也拥有团队密钥的加密副本,因此审计器也需要检查他们的用户密钥。
- 开放团队不会收到 CLKR,因为管理员会自动接受任何请求,因此这些团队不会被审计。同样,读者不允许轮换团队,因此他们也不审计团队。
- 客户端将最后看到的 Merkle 根签名到团队签名链链接中。然而,他们必须小心在执行团队轮换之前选择此 Merkle 根,否则,用户可能会在 key boxing 和选择 Merkle 根之间的时间内撤销设备。如果发生这种竞争,回顾过去的审计器会推断管理员为新的用户密钥进行了轮换,但实际上,管理员是为旧的用户密钥进行了加密。
- 审计器需要知道用户在哪些团队中,以便决定审计它们。我们不能信任服务器给我们的这些信息,它可能会恶意隐藏它不想让用户审计的团队。所以我们只需使用本地缓存中所有已加载的团队来选择要审计的团队。
- 审计可能因为正当理由而永久失败。想象一下,您加入了一个团队,所以它在您的本地团队缓存中,但后来您被从团队中移除了。现在当我们尝试进行审计时,它会失败,因为我们不在团队中,因此没有权限查看团队签名链。同时,服务器无法证明我们不在团队中,因为它不会向我们展示团队的签名链。由于这个原因,我们不能相信服务器说我们确实不在团队中的话,所以客户端只是安排重新审计。最终,团队审计将因为相同(正当)的原因失败 6 次并进入监狱。然而,这是可以的,因为用户永远不需要加载团队,因为他们反正不在团队中。如果他们后来被重新添加到团队中,团队加载期间的重新审计将会通过。
结论
团队 Box 审计器提供了强有力的保证,即即使在恶意服务器试图暗中避免被审计的情况下,团队成员设备撤销后团队的密钥也会被重新加密。