WebAssembly与回溯:Firefox 95中的细粒度沙箱技术
作者:Bobby Holley | 2021年12月6日
在Firefox 95中,我们推出了一项名为RLBox的创新沙箱技术——这是与加州大学圣迭戈分校和德克萨斯大学研究人员合作开发的成果。该技术能够轻松高效地隔离子组件,从而提升浏览器的安全性。相比传统的基于进程的沙箱技术,RLBox开辟了新的可能性。我们期待扩展其应用范围,并希望看到其他浏览器和软件项目采纳这项技术。
这项技术利用WebAssembly来隔离可能存在缺陷的代码,建立在去年向Mac和Linux用户推出的原型基础上。现在,我们将该技术推广到所有支持的Firefox平台(桌面和移动端),并隔离了五个不同的模块:Graphite、Hunspell、Ogg、Expat和Woff2[1]。
今后,我们可以将这些模块视为不可信代码。假设实施正确,即使其中任何一个模块存在零日漏洞,也不会对Firefox构成威胁。因此,我们更新了漏洞赏金计划,奖励研究人员即使在没有隔离库漏洞的情况下绕过沙箱的行为。
进程沙箱的局限性
所有主流浏览器都在独立的沙箱进程中运行Web内容,理论上防止其利用浏览器漏洞入侵您的计算机。在桌面操作系统上,Firefox还将每个站点隔离在独立的进程中,以保护站点之间的安全。
不幸的是,威胁行为者经常通过串联两个漏洞来攻击用户:一个用于攻破包含恶意站点的沙箱进程,另一个用于逃脱沙箱[2]。为了保护用户免受资金最充足的对手攻击,我们需要多层防护。
在已经沿信任边界进行隔离的基础上,下一步逻辑是跨功能边界进行隔离。历史上,这意味着将子组件提升到自己的进程中。例如,Firefox在专用的、锁定的进程中运行音频和视频编解码器,该进程与系统其他部分的接口有限。然而,这种方法存在一些严重限制。首先,它需要解耦代码并使其异步,这通常耗时且可能带来性能成本。其次,进程具有固定的内存开销,增加进程会增大应用程序的内存占用。
由于这些原因,没有人会认真考虑将XML解析器之类的东西提升到自己的进程中。要在这种粒度级别上进行隔离,我们需要不同的方法。
使用RLBox进行隔离
这就是RLBox的用武之地。我们不是将代码提升到单独的进程中,而是将其编译成WebAssembly,然后将该WebAssembly编译成本地代码。这并不意味着我们在Firefox中分发任何.wasm文件,因为WebAssembly步骤只是我们构建过程中的中间表示。
然而,这种转换对目标代码施加了两个关键限制:它不能跳转到程序其他部分的意外区域,也不能访问指定区域之外的内存。这些限制使得在可信和不可信代码之间共享地址空间(包括堆栈)变得安全,允许我们在同一进程中运行它们,基本上像以前一样。这反过来使得应用变得容易,无需重大重构:程序员只需要清理来自沙箱的任何值(因为它们可能是恶意制作的),RLBox通过污点层使这项任务变得简单。
这种转换的第一步很简单:我们使用Clang编译Firefox,而Clang知道如何发出WebAssembly,因此我们只需将给定模块的输出格式从本地代码切换为wasm。对于第二步,我们的原型实现使用了Cranelift。Cranelift非常出色,但第二个本地代码生成器增加了复杂性——我们意识到将WebAssembly映射回现有构建系统可以处理的东西会更简单。
我们通过wasm2c实现了这一点,它将WebAssembly直接转换为等效的C代码,然后我们可以将其与Firefox源代码一起反馈给Clang。这种方法非常简单,并自动启用许多重要功能,这些功能我们支持常规Firefox代码:配置文件引导优化、跨沙箱边界内联、崩溃报告、调试器支持、源代码索引,以及可能我们尚未意识到的其他功能。
下一步
RLBox在多个方面为我们带来了巨大收益:它保护用户免受意外缺陷和供应链攻击,并减少了我们在上游披露此类问题时匆忙应对的需要。因此,我们打算继续将其应用到更多组件。有些组件不适合这种方法——要么因为它们过于依赖与程序其他部分共享内存,要么因为它们对性能过于敏感,无法接受适度的开销——但我们已经确定了其他一些合适的候选组件。
此外,我们希望看到这项技术进入其他浏览器和软件项目,使生态系统更安全。RLBox是一个独立项目,设计得非常模块化和易于使用,其团队欢迎其他用例。
谈到团队:我要感谢Shravan Narayan、Deian Stefan和Hovav Shacham,他们不懈努力将这项工作从研究概念推向生产。向数亿用户分发是困难的,他们做了一些非常令人印象深刻的工作。
在加州大学圣迭戈分校雅各布斯工程学院网站上阅读更多关于RLBox和此公告的信息。
— [1] Graphite、Hunspell和Ogg的跨平台沙箱在Firefox 95中发布,而Expat和Woff2将在Firefox 96中发布。 [2] 通过使用系统调用来利用操作系统中的漏洞,或通过使用IPC消息来利用托管浏览器更高特权部分的进程中的漏洞。 —