RFC-0206:废弃存储器 | |
---|---|
状态 | 已接受 |
领域 |
|
说明 | 废弃存储服务,并迁移客户端以使用存储功能 |
问题 | |
Gerrit 更改 | |
作者 | |
审核人 | |
提交日期(年-月-日) | 2022-12-08 |
审核日期(年-月-日) | 2022-01-13 |
摘要
隐藏服务未进行维护,并且当前实现未提供它最初创建时用于提供的属性。此 RFC 建议了废弃 stash。我们会将使用 stash 的客户端迁移到 Fuchsia 的标准永久性存储空间功能,然后删除提供 stash 服务的三个组件。
设计初衷
Stash 是一项简单的持久性服务,最初是供需要在启动过程早期(通常在软件更新之前)访问永久性可变状态的平台组件使用。
正如下面更详细地讨论的那样,隐藏协议存在三种不同的变体。所有变体都使用相同的底层 FIDL 协议,并且使用相同的二进制文件实现。本文档使用“stash”一词来涵盖所有这些变体。
在软件更新之前,Fuchsia 每次读取永久性可变状态时,攻击者都可能会保留其访问权限:如果攻击者在上次重启中获得了设备的完全控制权,我们必须假设他们能够将任意数据写入设备的永久性可变状态。读取可变数据的代码路径中有一个漏洞,这些由攻击者控制的数据可能会在下次重启时入侵系统。如果读取发生在启动过程的早期,后果可能更严重;攻击者可能会阻止软件更新进行,从而阻止漏洞进行修补。
Stash 旨在通过提供简单且安全的永久性可变存储空间供前期启动使用,从而降低这种情况的风险。Stash 旨在具有以下三个属性:
- 最小 - Stash 应使用比完整存储堆栈小得多、更简单的代码库,从而使实现更易于审核,并降低出现错误的风险。
- 易于使用 - Stash 应便于客户使用,从而降低在与永久性可变状态集成时出现 bug 的风险。
- 安全 - 由于密室的存在是为了提高系统的安全性,因此其设计和实现不应引入新的安全问题。
目前,Sash 无法提供以下大多数属性:
- 最小 - Stash 作为 FIDL 服务器实现,位于序列化层之上,基于标准文件系统(基于 zxcrypt 的 fxfs 或 minfs)。Stash 并不比标准存储堆栈简单。从理论上说,将来可以在不更改 FIDL 接口的情况下迁移到更简单的存储实现,但没有这样做的计划。
- 易于使用 - Stash 主要用于实现此目标。其 FIDL 接口公开了具有有限数量数据类型的简单键值对范式。这样可以简化客户端代码,并降低客户端代码中出现错误的可能性。处理向后兼容性、事务性写入和 FIDL 错误的需求确实会带来一定程度的复杂性,而且在客户端仍需要编写自己的帮助程序库(例如 wlan)的情况下。
- 安全 - 隐蔽的设计假设组件框架将提供客户端身份,使 FIDL 服务器能够识别其客户端。组件框架不提供客户端身份,而且在可预见的未来也没有推出此功能的计划。这意味着不同的隐藏客户端可以相互读取和写入对方的数据,协议依赖受理系统来避免这种情况。
尽管 stash 最初仅用于支持“前期启动”组件,但 Fuchsia 上并没有针对前期启动阶段的正式定义,也未就哪些组件被视为“前期启动”组件的清单。一些暗藏客户端不会在启动过程中提前启动,并且现有客户端也不会在引导期间启动。
自 2020 年以来,Fuchsia 管理了一个复杂的复制和隔离系统,以解决上述安全问题(请参阅 https://fxbug.dev/42124367)。
- 定义了三种不同的 FIDL 存储协议:
fuchsia.stash.Store
、fuchsia.stash.Store2
和fuchsia.stash.SecureStore
。 - 有三个不同的存储组件,每个组件作为单独的进程运行,并提供单独的协议。
- 存储客户端会被仔细分配给这三个协议之一,并评估共享一个通道的客户端,以确保这些客户端读取或写入彼此数据的风险在可接受范围内。
- 我们提供了一个 build 可见性列表,旨在防止在没有进行安全审核的情况下添加新的隐藏客户端。
这种情况会导致持续的混乱和工程成本,而其性能和安全属性则几乎不尽人意。
利益相关方
教员:
- Hjfreyer
审核者:
- atait (DHCP)
- brunodalbo (Netstack)
- ecstone(迁移)
- 酋长国(场景)
- jamuraa(蓝牙)
- nmccracken (WLAN)
- palmer(安全)
- senj(Omaha 客户端)
- paulfaria(设置服务)
咨询人员:
- silberst、wittrock、shayba、cgonyeo、erahm、mnck、jfsulliv
社交:
此 RFC 的早期草稿是我们与所有受影响客户的利益相关方合作编制的。
设计
我们计划弃用存储,并将大多数现有客户端迁移到其他组件使用的“数据”存储功能。您可以使用标准文件系统 API 访问存储功能。对于大多数组件,此迁移都会使用序列化库将组件的永久性数据结构转换为该组件随后写入磁盘的字节流。读取永久性数据涉及从磁盘读取文件,然后使用同一个库反序列化和填充组件的永久性数据结构。
Seash 并没有将 stash 用于预期用途(请参阅 https://fxbug.dev/42173164),我们会将其用例迁移到结构化配置开发者替换项。
我们始终建议尽量减少在启动前期启动且会影响软件更新的成功的组件的攻击面。目前依赖 stash 的网络和 SWD 组件在使用数据存储功能时应保持谨慎,但我们不会针对这些组件制定一套单独的存储空间访问权限要求。安全地将数据持久存储到某个存储功能的最佳实践包括:
- 尽量减少持久保留的数据量。
- 使用窄且精确定义的数据类型。
- 使用已通过安全审核的序列化库打包和解压缩数据。
您可以根据所需的隐藏属性评估此设计,如下所示:
- 最小 - 整体复杂性与现状相似:仍会使用序列化库(但使用位置从公共位置移至每个客户端)。Fxfs(或 minfs 和 zxcrypt)仍然存在。将来,您仍然可以通过更简单的文件系统支持存储功能,但这样做可能需要在客户端进行更改。
- 易于使用 - 易用性与现状相似:客户端必须与序列化库进行交互并执行基本的文件系统操作,而不是管理 FIDL 连接、事务和故障。使用存储功能保持 Fuchsia 组件状态的多种现有实现可以重复使用。
- 安全性 - 安全性得到提升:该设计保证了组件之间的隔离。我们使用已通过安全审核并在生产环境中使用的现有 Fuchsia 技术。
此外,该设计还改进了几个其他属性:
- 我们移除了三个组件实例,因此资源利用率较低
- 更容易将磁盘利用率归因于使用磁盘的组件
实现
如果此方案被接受,我们会明确将存储组件和所有隐藏协议记录为已弃用。
然后,我们将分别与七个受影响的客户端组件共同商定迁移计划和大致时间表。在某些情况下,团队已经在计划停止存储(例如 https://fxbug.dev/42172963),而在其他情况下,可信平台服务团队可以帮助您进行迁移。此 RFC 未指定完成迁移的截止日期。
将关键数据存储在隐密状态的组件需要有跳跃式版本才能完成迁移(即每台设备在其软件升级过程中都必须通过的软件版本)。在此阶梯式发布阶段,组件能够从 stash 或存储 capability 中读取其持久状态,但会向存储 capability 中写入数据。 通过此跳步步骤可确保在组件移除其代码以读取藏书之前,将数据迁移到存储功能。
在我们与存储客户端合作规划其迁移的过程中,我们的目标是尽可能减少所需的平台跳板版本数量。
特定存储协议的所有客户端均完成迁移后,该协议及其存储服务实例将被删除。此存储二进制文件将与最后一个协议一并删除。
性能
此方案将删除三个组件实例,以减少磁盘、内存和 CPU 利用率。
安全注意事项
该方案通过保证前期启动组件不再读取和写入彼此的持久状态,并消除当前未维护的代码,从而提高 Fuchsia 的安全性。
隐私注意事项
此方案不会更改系统收集和存储的用户数据集。 由于前期启动组件遭到入侵,其隐私性得到了小幅改进,无法再读取由其他组件存储的 PII 数据。
测试
现有的端到端测试和集成测试涵盖了使用 stash 存储和检索永久性可变状态的功能。这些测试将使用基本存储功能来存储和检索此状态。每个存储客户端都应添加集成测试,以验证从存储存储区到存储功能的迁移。
文档
迁移完成后,现有存储文档将被删除。
考虑的替代方案
替代方案 1:使用新的“基本”存储空间功能
当前方案建议使用现有的“数据”存储功能。类似的解决方案是创建一个新的“基本”存储空间,专门供前期启动组件使用。
通过使用单独的存储功能,我们可以跟踪最终应使用更简单存储解决方案的组件。使用“基本”存储功能的组件遵循一系列最佳实践,旨在降低复杂性、降低出现错误的风险,并简化迁移到未来替代后端的流程。最佳实践可能包括:
- 使用已获批准的序列化库打包和解压缩数据。例如,Rust 组件的 serde
- 以原子方式更新文件的内容。例如,您可以写入一个临时文件,然后将该临时文件重命名为最终路径
- 请勿创建子目录
- 请勿创建大于 X kB 的文件
- 请勿创建超过 Y 个文件
其中许多最佳实践有助于简化我们可在替换文件系统中进行的简化,以支持“基本”存储功能。例如,如果客户端不使用目录,迁移到不支持目录的简单文件系统会更加容易。
安全团队维护一份列表,其中列出了哪些组件会被视为“提前启动”。自动化工具会验证这些组件是否仅使用“基本”存储功能,并在实际情况下验证它们对文件系统的使用情况是否与最佳实践一致。
此解决方案的初始实现很简单:“basic”可以由现有“data”fxfs 或 minfs 分区上的新子目录支持。但是,要保持一致的“前期启动”定义,并打造相应工具以在这些前期启动组件中强制实施数据持久性模式,就需要支付高昂的流程和工具成本。
Fuchsia 不打算实现更简单的文件系统来支持“基本”存储功能。如果没有不同的文件系统,花费资源来维护由同一实现提供支持的两组不同客户端几乎没有什么好处,因此未选择此解决方案。
替代方案 2:为早期可变状态编写专用客户端库
当前方案建议使用现有的成熟库来序列化可变的持久性状态。另一种方法是使用每种目标语言(目前支持 Rust 和 Go,将来可能会使用 C++)编写一个新的客户端库。
专用客户端库可以轻松执行最佳实践,并且可能比 serde 等现有的通用序列化库更小且更易于使用。不过,设计和实现这些新库将显著增加该方案的工程成本。现有客户端通过不同的封装容器将 stash 用于不同目的,因此单个客户端库可能无法满足所有这些客户端的预期。Stash 目前无人值守,如果承诺设计和实现新客户端库,迁移可能会延迟几个季度。