Firefox 100 进程隔离改进

简介

Firefox 使用 多进程模型 来提高浏览时的安全性和稳定性:网页内容(如 HTML/CSS 和 Javascript)在与操作系统隔离的单独进程中呈现,并由特权父进程管理。这样,攻击者利用内容进程中的漏洞所获得的控制权就受到了限制。

自从我们部署了这个模型以来,我们一直在努力改进内容进程的隔离,以进一步限制攻击面。这是一项具有挑战性的任务,因为内容进程需要访问一些操作系统 API 才能正常运行:例如,它们仍然需要能够与父进程通信。 

在这篇文章中,我们想深入探讨一下我们取得的最新重大里程碑: Win32k 锁定,它在 Windows 上运行时极大地减少了内容进程的功能。加上之前发布的两个主要早期努力(FissionRLBox),这完成了一系列巨大的飞跃,将显著提高 Firefox 的安全性。

虽然 Win32k 锁定 是一种 Windows 特定的技术,但它之所以成为可能,是因为 Mozilla 在过去四年左右的时间里对 Firefox 安全边界进行了重大重构,这使得在其他操作系统上也能实现类似的安全改进。

目标:Win32k 锁定

Firefox 在 Windows 上运行时,对渲染网页内容的进程施加了一些限制,限制它们可以执行的操作。不幸的是,默认情况下,它们仍然可以访问整个 Windows API,这会打开一个很大的攻击面:Windows API 包含许多部分,例如,处理线程、进程和内存管理的核心部分,还有网络和套接字库、打印和多媒体 API 等等。

我们特别关注的是 win32k.sys API,其中包含许多图形和窗口相关的系统调用,这些系统调用一直以来都是可利用的。追溯到 Windows 的起源,这种情况很可能是由于微软将许多原本在用户模式下运行的操作迁移到内核中,以提高 Windows 95 和 NT4 时代周围的性能。

这些 API 很可能在最初设计时并没有考虑到这种敏感的上下文,因此一直以来都是黑客们用来突破应用程序沙箱并进入内核的传统目标。

在 Windows 8 中,微软引入了一种名为 PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY 的新缓解措施,应用程序可以使用它来禁用对 win32k.sys 系统调用的访问。这个名字太长了,一直重复说起来很麻烦,所以我们以后将用我们内部的名称来指代它:“Win32k 锁定“。

所需的工作

在 Web 内容进程(最容易受到潜在恶意网页和 JavaScript 攻击的进程)上设置 Win32k 锁定标志,意味着这些进程将无法再执行任何图形、窗口管理、输入处理等操作。

为了完成这些任务,这些操作必须远程到拥有必要权限的进程,通常是拥有 GPU 访问权限并处理合成和绘制的进程(以下称为 GPU 进程),或者特权父进程。 

绘制网页:WebRender

为了绘制网页的内容,Firefox 历史上使用过各种方法来与 Windows API 交互,从使用现代基于 Direct3D 的纹理,到回退到 GDI 表面,最后再降级到纯软件模式。

这些不同的选项需要相当多的工作才能远程化,因为 Win32k 锁定下大部分图形 API 都无法使用。好消息是,从 Firefox 92 开始,我们的渲染栈已切换到 WebRender,它将所有实际的绘制操作从内容进程迁移到 GPU 进程中的 WebRender。

由于 WebRender 中的内容进程不再需要直接与平台绘制 API 交互,因此避免了任何与 Win32k 锁定相关的 problems。WebRender 本身在设计上部分 更类似于游戏引擎,因此,不太容易受到驱动程序错误的影响

对于剩下的那些太糟糕以至于毫无用处的驱动程序,它仍然有 完全基于软件的模式,这意味着我们没有其他回退方案需要考虑。

网页绘制:Canvas 2D 和 WebGL 3D

Canvas API 为网页提供绘制 2D 图形的能力。在最初的 Firefox 实现中,这些 JavaScript API 在 Web 内容进程中执行,对 Windows 绘制 API 的调用直接来自同一个进程。

在 Win32k 锁定场景中,这不再可能,因此 所有绘制命令都通过 IPC 在 GPU 进程中记录和回放,实现远程化

虽然最初的实现性能良好,但仍有来自某些网站的性能下降的报告(变得更快的网站通常不会抱怨!)。一个特别的痛点是,那些重复调用 getImageData() 的应用程序:将 Canvas 远程化意味着现在必须从另一个进程获取 GPU 纹理并通过 IPC 发送。

我们在 getImageData 在帧开始时被调用时补偿了这种情况,通过检测这种情况并主动准备合适的表面,使从 GPU 的复制速度更快。

除了 Canvas API 来绘制 2D 图形外,网页平台还提供了一个 用于进行 3D 绘制的 API,称为 WebGL。WebGL 是一个状态繁重的 API,因此正确且高效地同步子进程和父进程(以及父进程和驱动程序)需要 非常 小心

WebGL 最初在 Content 中处理所有验证,但是由于 GPU 和相关的攻击面从那里删除了,我们需要在子进程和父进程之间创建一个健壮的验证 API,才能获得完整的安全优势。

(非)原生表单主题

HTML 网页有能力显示表单控件。虽然绝大多数网站都为这些表单控件提供 自定义外观和样式,但并非所有网站都这样做,如果没有,你就会得到一个像(最初是!) 操作系统原生元素 那样样式的输入 GUI 小部件。

历史上,这些都是通过从内容进程中调用相应的 OS 小部件 API 来绘制的,但在 Win32k 锁定下这些 API 无法使用。

这不能通过远程调用来轻易修复,因为小部件本身有无限的尺寸、形状和样式,可以交互,并且需要对用户输入做出响应并分发消息。我们决定让 Firefox 自己绘制表单控件,使用跨平台的样式。

虽然更改表单控件的外观会对网页兼容性产生影响,而且有些人更喜欢更原生的外观——在少数没有为控件应用自己样式的页面上——Firefox 的方法与其他浏览器所采用的方法一致,这可能是由于考虑因素非常相似。

滚动条曾经是一个特别令人头疼的问题:我们不想以与其他用户体验不同的方式绘制内容窗口的主滚动条,因为嵌套的滚动条会以不同的样式显示,看起来很奇怪。但是,与相当罕见的非样式表单小部件不同,主滚动条在大多数网页上都是可见的,并且由于它在概念上属于浏览器用户体验,我们真的希望它看起来是原生的。

因此,我们决定绘制所有滚动条以匹配系统主题,尽管如何处理即使 操作系统供应商似乎也无法决定“原生”外观是什么 仍然是一个开放性问题。

最后的障碍

换行

通过以上更改,我们认为我们已经将所有通常访问 win32k.sys 中图形和窗口小部件 API 的内容都包含在内,因此我们开始在禁用 win32k 系统调用后运行完整的 Firefox 测试套件。这导致至少出现了一个意外错误:Firefox 在 尝试为某些使用复杂脚本的语言查找换行符时崩溃

虽然 Firefox 能够自行正确地确定大多数语言中多字节字符流中的词语结尾,但对泰语、老挝语、藏语和高棉语的支持众所周知是不完善的,并且 在这些情况下,Firefox 可以要求操作系统为其处理换行符。 但至少在 Windows 上,执行此操作的功能受 Win32k 锁定开关的限制。糟糕!

正在 努力将 ICU4X 整合进来,并将所有与国际化相关的功能都建立在它之上,这意味着 Firefox 将能够完美地处理所有脚本而无需涉及操作系统,但这项工作量很大,而且不清楚它是否会导致 win32k 锁定的推出延迟。

我们尝试通过 IPC 传递换行符做了一些实验。最初,这会导致性能下降,但当我们 添加缓存 后,性能令人满意,有时甚至有所提高,因为现在可以在许多情况下避免操作系统调用。

DLL 加载和第三方交互

禁用 win32k.sys 访问的另一个复杂之处在于,如此多的 Windows 功能默认情况下都假设它可用,并且必须采取具体措施来确保相关的 DLL 在启动时不会被加载。例如,Firefox 本身不会加载包含一些 win32k API 的 user32 DLL,但是 注入的第三方 DLL 有时会这样做。 这会导致问题,因为 特别是 COM 初始化会使用 win32k 调用来获取窗口站和桌面,如果 DLL 存在的话。 这些调用将在启用 Win32k 锁定后失败,并静默地破坏 COM 以及依赖它的功能,例如我们的辅助功能支持。

在 Windows 10 Fall Creators Update 及更高版本上,我们有一个修复程序可以阻止这些调用并强制执行回退,从而确保一切正常运行。我们测量到 不加载 DLL 会导致打开新选项卡时大约提高 15% 的性能,除了安全优势之外,还带来了不错的性能提升。

剩余工作

正如上一节中提到的,Win32k 锁定最初将在 Windows 10 Fall Creators Update 及更高版本上推出。在 Windows 8 和未修补的 Windows 10(不幸的是,这似乎仍在使用!)上,我们仍在测试针对第三方 DLL 干扰情况的修复程序,因此对它们的支持将在 未来的版本中推出。

对于 Canvas 2D 支持,我们仍在 研究如何提高应用程序的性能,这些应用程序在进程重新分配后性能下降。 同时,我们正在进行实验,以了解是否可以通过 WebGL 实现 Canvas 2D 的硬件加速,这将增加 2D 和 3D 实现之间的代码共享,并利用现代视频驱动程序针对 3D 场景进行了更好的优化。

结论

在像 Firefox 这样的大型应用程序中对职责分离进行重大更改的改造是一项艰巨的多年工程挑战,但这对于提升浏览器安全性和继续保护用户安全来说绝对是必要的。我们很高兴能够完成这项工作,并在 Firefox 100 中向您展示结果。

其他平台

如果您是 Mac 用户,您可能会想知道是否可以对 macOS 做出类似于 Win32k 锁定的操作。您是对的,而且我有一个好消息要告诉您:我们在 Firefox 95 中已经悄然 发布了阻止访问 WindowServer 的更改,提高了安全性,并使进程启动速度提高了约 30-70%。 这也成为可能,因为上述远程 WebGL 和非原生主题工作。

对于 Linux 用户,我们 从内容进程到 X11 服务器的连接,这阻止了攻击者利用不安全的 X11 协议。尽管 Linux 发行版一直在转向更安全的 Wayland 协议作为默认协议,但我们仍然看到很多用户使用 X11 或 XWayland 配置,因此这绝对是件好事,它在 Firefox 99 中发布。

我们正在招聘

如果您发现上面的技术背景故事很有趣,我想指出,我们的操作系统集成和强化团队很快就会招聘。我们特别寻找有经验的 C++ 程序员,他们对 Rust 有些兴趣,并且精通 Windows 编程。

如果您符合此描述,并且有兴趣与我们一起在 Firefox 安全领域迈出下一步,我们建议您关注我们的 职业页面

感谢 Bob Owen、Chris Martin 和 Stephen Pohl 对本文的技术贡献,以及他们与 Kelsey Gilbert 和 Jed Davis 一起为实现这些安全改进而付出的所有努力。

关于 Gian-Carlo Pascutto

更多 Gian-Carlo Pascutto 的文章…


5 条评论

  1. Peter

    我几年后又回到了 Firefox,我感到非常高兴

    2022 年 5 月 12 日 下午 3:51

  2. Tobias

    我们使用 Microfoft Defender for Endpoint 和漏洞利用防护。不幸的是,自 100 版以来 Firefox 无法正常工作。它会启动,但不会渲染任何网站。所有关于页面或设置菜单也都无法加载。
    我们看不到 Firefox 的任何被阻止的组件。也许您听说过这些问题与 Microfoft Defender for Endpoint 或类似产品结合使用的情况吗?

    2022 年 5 月 16 日 上午 4:30

    1. Gian-Carlo Pascutto

      Firefox 与“漏洞利用防护”存在一些已知的不兼容问题,尤其是在使用非默认设置时:https://support.mozilla.org/nl/kb/compatibility-exploit-protection-windows-10

      请注意,Firefox 本身已经包含了大多数这些漏洞利用缓解措施,这些措施是已知与正常浏览器操作完全兼容的。

      目前尚无关于 win32k 锁定的已知不兼容问题。由于 win32k 锁定只影响渲染(不受信任的)Web 内容的进程,因此缺少设置菜单似乎表明了另一个问题。

      您可以在 https://bugzilla.mozilla.org/enter_bug.cgi?product=External%20Software%20Affecting%20Firefox 中提交错误报告,以便我们进一步调查吗?

      2022 年 5 月 16 日 上午 7:58

      1. Gian-Carlo Pascutto

        更新:经过调查,我们发现 Microsoft 有一段时间(Windows 10 版本 1709 安全基线)的漏洞利用防护配置文件存在问题。该文件已被 Microsoft 撤销,但运行该破损版本的安装程序将在 Firefox 100 更新后崩溃。从 Microsoft 获取更新的漏洞利用防护安全基线设置,地址:https://www.microsoft.com/en-us/download/details.aspx?id=55319,应该可以解决此问题。

        2022 年 5 月 23 日 上午 10:38

  3. Marco

    太棒了,谢谢!

    2022 年 5 月 27 日 上午 5:28

本文的评论已关闭。