关于作者。 Eric Rescorla-Mozilla Firefox Group技术总监最近,当大多数附加组件(扩展程序,附加组件)停止工作时,Firefox中发生了一个事件。 这是由于我们方面的错误:我们没有注意到用于对附加组件进行签名的证书之一已经过期,从而导致绝大多数证书都断开了连接。 既然我们已经解决了问题,并且大多数附件都已还原,那么我想详细说明发生了什么,为什么以及如何解决它。
供参考:扩展名及其签名
尽管许多人都直接使用Firefox,但浏览器也支持强大的扩展机制。 他们在Firefox中添加了第三方功能,以扩展我们默认提供的功能。 当前有超过15,000个Firefox插件:从
广告拦截到
管理数百个标签 。
Firefox要求所有安装的附加组件都必须进行
数字签名 。 此要求旨在通过要求Mozilla员工进行最低标准验证来保护用户免受恶意扩展。 在2015年引入这项要求之前,我们曾
遇到恶意扩展的
严重问题 。
签名通过Firefox的预安装“根证书”工作。 它离线存储在
硬件安全模块(HSM)中 。 每隔几年,它将用于对新的“中间证书”进行签名,该证书将在线存储并在签名过程中使用。 提交扩展名以供签名时,我们将生成一个新的临时“最终实体证书”,并使用中间证书对其进行签名。 然后使用目标证书对扩展名进行签名。 在视觉上,它看起来像这样:
请注意,每个证书都有一个“主题”(证书所属)和一个“发布者”(签名人)。 对于根证书,这是一个相同的证书,但是对于其他证书,发布者是对其进行签名的对象。
这里的重点是,每个附加组件都使用其自己的最终对象证书签名,但是几乎所有附加组件都具有相同的中间证书(几个非常旧的附加组件由另一个中间链接签名)。 这就是问题所在:每个证书都有固定的到期日期。 在此窗口之前或之后,该证书将不被接受,并且此证书签名的扩展名不能上传到Firefox。 不幸的是,我们使用的中间证书于5月4日1:00 UTC到期,此证书签名的每个附加组件都立即变为未验证状态,无法上载到Firefox。
尽管所有附加组件都在大约凌晨1点过期,但是并没有立即感觉到后果。 原因是Firefox不会不断检查插件的有效性。 大约每24小时检查一次,验证时间因每个用户而异。 结果,有些人马上就遇到了问题,有些则很晚了。 我们在Mozilla于5月3日(星期五)太平洋标准时间下午6:00首先了解了此问题,并立即成立了一个小组来纠正这种情况。
损坏极限
一旦我们了解了所面临的情况,我们就采取了一些措施来避免局势恶化。
首先,我们关闭了新添加项的签名。 那时是合理的,因为签名放置了无效的证书。 往回看,似乎可以保留此功能,但是事实证明,它也与“硬日期固件”的软化冲突,我们将在下面进行讨论(尽管最后我们没有使用它)。 因此,最好保留此选项。 因此,新添加的签名现在被延迟。
其次,我们立即发布了一个快速修复程序,该更新程序禁止重新验证扩展签名。 这样做的目的是保护尚未经过重新测试的用户。 我们在进行其他任何修复之前都已执行此操作,现在在修复可用时将其删除。
并行工作
从理论上讲,解决该问题的方法看起来很简单:制作一个新的有效证书,并使用此证书重新发布每次添加的证书。 不幸的是,我们很快就确定这行不通,原因有很多:
- 有很多扩展(超过15,000个),并且该服务并未针对批量签名进行优化,因此仅对每个附加组件重新签名将花费比我们想要的更多的时间。
- 加载项签名后,用户将需要获得一个新的加载项。 其中一些托管在Mozilla服务器上,Firefox会在24小时内对其进行更新,但是用户必须手动更新从其他来源安装的所有附件,这非常不方便。
取而代之的是,我们专注于尝试开发一种解决方案,该解决方案可以在用户很少或不需要人工干预的情况下解决此问题。
考虑了多种方法后,我们迅速同意了并行执行的两个主要策略:
- Firefox补丁程序,用于更改用于验证证书的日期。 在这种情况下,现有的附件将再次神奇地工作,但是将需要提供新版本的Firefox。
- 生成一个新的有效证书,并以某种方式说服Firefox接受它,而不是现有的过期证书。
我们不确定确切的工作方式,因此我们决定并行执行工作并实施第一个工作,这似乎是一个可行的解决方案。 最终,我们完成了第二个修订的部署-新证书,我将对其进行详细描述。
更换证书
如上所述,有两个主要步骤可以遵循:
- 创建一个新的有效证书。
- 在Firefox中远程安装。
要了解其工作原理,您需要进一步了解Firefox如何检查加载项。 附件本身以文件包的形式出现,其中包括用于签名的证书链。 因此,将独立检查插件是否知道根证书,该证书是在构建过程中在Firefox中配置的。 但是,正如我说的那样,中间证书已损坏,因此该附加组件并不是真正可验证的。
但是事实证明,当Firefox尝试验证扩展时,它不仅限于在扩展本身中使用证书。 相反,它尝试创建一个有效的证书链,从端点证书开始,一直到根目录。 该算法很复杂,但是从高层次上讲,您首先要获得最终对象的证书,然后再找到一个主题与最终对象的证书的发布者(即中间证书)相等的证书。 在简单的情况下,这只是外接程序随附的中间链接,但它可以是浏览器知道的任何证书。 如果我们可以远程添加新的有效证书,则Firefox也将尝试构建这样的链。 下图显示了在安装新证书之前和之后的情况。

安装新证书后,Firefox有两个检查证书链的选项:使用旧的无效证书(将不起作用)或使用新的有效证书(将起作用)。 这里的一个重要功能是,新证书与旧证书具有相同的主题名称和公共密钥,因此在最终对象的证书上的签名是有效的。 幸运的是,Firefox非常聪明,可以尝试这两种方法,直到找到可行的方法为止,因此该扩展名再次有效。 请注意,这与验证TLS证书所用的逻辑相同,因此,这是我们能够使用的相对容易理解的代码(熟悉WebPKI的读者将理解交叉验证以这种方式工作)。
关于此修复程序的妙处在于,它不需要更改任何现有扩展名。 当我们在Firefox中安装新证书时,即使具有旧证书的扩展程序也可以通过测试。 在Firefox中提供新证书的诀窍是自动和远程进行,然后让Firefox仔细检查所有可能已禁用的扩展。
诺曼底和研究系统
具有讽刺意味的是,解决该问题的方法是一种特殊的扩展,称为
系统附件 (SAO)。 为了研究受众(研究),我们之前开发了一个名为Normandy的系统,该系统可以将SAO提供给Firefox用户。 这些SAO在用户的浏览器中自动执行。 尽管它们通常用于实验,但它们也可以广泛访问Firefox中的内部API。 在这种情况下,务必将新证书添加到Firefox用于检查扩展名的证书数据库中(技术说明:我们不添加具有任何特殊特权的证书;它通过使用根证书签名来获得其特权。我们只需添加它到Firefox可以使用的证书池中,因此我们不会在Firefox中添加新的特权证书。
因此,这里的解决方案是创建一个执行以下两项操作的SAO:
- 安装我们制作的新证书。
- 使浏览器重新检查每个加载项以激活已断开连接的加载项。
但是等等,你说。 加载项不起作用,那么如何使SAO起作用? 好吧,我们将用新证书对其进行签名!
放在一起……为什么这么久?
因此,现在我们有一个计划:发布新证书以替换旧证书,构建一个系统插件以将其安装在Firefox中,然后将其部署到Normandy。 我们于太平洋标准时间(PST)于5月3日星期五下午6:00开始工作,并将修复程序在凌晨2:44左右(即不到9小时)发送给诺曼底,然后又花了6到12个小时才让大多数用户收到。 这实际上是一个很好的开始,但是我在Twitter上看到了一系列问题,为什么我们不能更快地做到这一点。 有许多步骤很耗时。
首先,签发新的中间证书花了一些时间。 如上所述,根证书位于硬件安全模块中,该模块脱机存储。 这是一种很好的安全做法,因为您很少使用根证书,因此希望保持其安全。 但是显然,当您在紧急情况下需要签发新证书时,这样做有些不便。 无论如何,我们的一名工程师必须去一个安全的地方,存放HSM。 然后,当我们无法颁发正确的证书时,出现了几次错误的开始,并且每次尝试都需要进行一两个小时的测试,然后才能确切知道该怎么做。
其次,系统开发需要一些时间。 从概念上讲,一切都非常简单,但是即使是简单的程序也需要谨慎,我们确实想确保我们不会使情况恶化。 在发送SAO之前,有必要对其进行测试,这需要花费时间,尤其是考虑到需要对其进行签名时。 但是签名系统已禁用,因此我们必须寻找解决方法。
最终,一旦SAO准备交付,就需要花费一些时间进行部署。 Firefox客户端每6个小时检查一次诺曼底更新,当然,许多客户端都处于脱机状态,因此更新没有立即分发给所有Firefox用户。 但是,目前,大多数人都收到了更新和/或新版本,我们稍后将其发布。
最后步骤
尽管通过研究系统部署的系统插件可以纠正大多数用户的情况,但并不能覆盖所有人。 特别是,几种类型的用户需要使用不同的方法:
- 禁用遥测或研究的用户。
- 没有针对Android的Firefox用户(Fennec)。
- 后续版本的Firefox ESR的用户不订阅遥测报告。
- 支持HTTPS MiTM代理的用户,因为我们的附加安装系统会强制这些连接使用密钥,这与代理冲突。
- 研究系统无法访问的非常老版本的Firefox的用户。
对于最后一组,我们无能为力-他们将必须升级到新版本的Firefox,因为旧版本通常具有相当严重的未修补安全漏洞。 我们知道有些人选择使用旧版本的Firefox是因为他们想运行旧样式的扩展,但是现在许多人都可以使用新版本的Firefox。 对于其他组,我们为Firefox开发了一个补丁,该补丁将在升级后安装新证书。 它也作为“点缀”的Firefox新版本发布,因此人们应该通过常规更新渠道获得它-可能已经收到了它。 如果您具有下游版本,则需要等待维护者进行更新。
我们承认,这都不是完美的。 特别是在某些情况下,用户会丢失与附加组件相关的数据(例如,
诸如“具有多个帐户的容器”之类的
扩展名 )。
我们无法开发出可避免这种副作用的补丁程序,但我们认为从短期来看,这是大多数用户的最佳方法。 从长远来看,我们将寻找解决此类问题的最佳架构方法。
上课
首先,我想说的是,团队在此方面做得非常出色:他们在最初报告发布后不到12小时的时间内开发并发送了修复程序。 作为参加会议的人员,我可以说人们在困难的情况下辛勤工作,浪费了很少的时间。
鉴于此,很明显,这不是理想的情况,这根本不应该发生。 我们显然需要调整流程,以减少发生此类事件和类似事件的可能性,并促进对其进行纠正。
下周,我们将进行正式汇报,并发布我们打算进行的更改的列表,但是现在,这是我对这个主题的初步想法。 最重要的是,我们应该有一种更好的方法来监视Firefox中潜在定时炸弹的所有系统的状态。 您需要确保它们都不突然停止工作。 我们仍在这里处理细节,但至少我们需要对此类系统进行清点。
其次,我们需要一种机制来快速更新用户,即使在其他任何情况都不起作用时,
尤其是在这种情况下也是如此。 能够使用研究系统非常好,但是它也不是我们投入使用的最完美的工具,并且会产生一些不良的副作用。 特别是,我们知道许多用户已打开自动更新,但他们不希望参加研究,这是一个合理的偏好(更不用说,我是用这种方式设置浏览器的!),但是同时,我们应该能够推送更新。 无论采用哪种内部技术机制,用户都应该能够选择更新(包括修复程序),而放弃其他所有内容。 另外,更新通道应该更快。 即使在星期一,我们仍然有一些用户没有选择此修复程序或新版本,这显然不是完美的。 我们已经解决了这个问题,但是此事件表明了它的重要性。
最后,我们将对扩展安全体系结构进行更一般的研究,以确保它正确地提供安全性,并且将故障风险降到最低。
下周,我们将发布对此情况进行更全面分析的结果。