摘要

  • 加载cdn.polyfill.io的站点所做的不仅仅是引用一个开源项目。它允许实时远程服务选择 JavaScript 并返回以供在站点的浏览器上下文中执行。因此,信任对象包括域名、运营商、路由和响应路径,而不仅仅是可以在其他地方检查的源代码。[15][16][17]
  • 2024 年 2 月,Polyfill.io 域名及相关项目控制权发生变更。Fastly、Cloudflare 和一个 FormatJS 项目当时做出了反应,表明在 6 月公开报告恶意交付之前,下游重新评估是可能的。转让本身不应被描述为恶意意图的证据。[2][3][4]
  • Sansec 于 6 月 25 日报道称,该服务有选择地返回修改后的 JavaScript,将符合条件的访问者重定向。Cloudflare 表示 Page Shield 指示器匹配从 6 月 8 日就开始了,而 Akamai 描述了服务器端采样和客户端检查。这些观察证实了重定向活动,而非远程提供 JavaScript 理论上可以执行的每一个操作。[1][5][9]
  • Sansec 估计超过 100,000 个站点引用了该服务。Cloudflare 引用的估算接近 4% 的网站。这两个数字都不是提供恶意分支、重定向访问者或遭受损失的确证网站数量。[1][5]
  • Cloudflare 的自动重写和 Namecheap 对该域名的持有限制了交付路径。但它们并未从下游代码中删除过时的引用,确定每个先前访问者收到什么,也未完成每个网站所有者的调查。[5][6][10]
  • Fides、Jellyfish 和 Wordfence 说明了三种不同的证据任务:确定条件路径是否可达,追踪传递供应商,验证移除,以及避免将对端点的引用视为利用证据。[11][12][13][14][22]
  • 此处的问责不是法律结论或同等责任的主张。它意味着每一方应为自己实际能够行使的控制负责。网站所有者保留了对必要性、清单、供应商选择、自托管、所有权监控、浏览器端观察、调查、修复和沟通的控制权。

脚本标签委托了权限,而不仅仅是便利

这一核心技术决策看似普通。网站在页面中放置了一个script元素,并将其指向cdn.polyfill.io。当访客加载该页面时,浏览器从远程服务请求 JavaScript。该服务可以检查请求特性并提供适合浏览器的 polyfill,使旧版浏览器能够使用它们原本没有原生实现的 Web 功能。

这种安排解决了一个实际的兼容性问题。网站无需向每个访客提供所有兼容函数,而是可以请求专门的服务返回特定浏览器似乎需要的内容。结果可能比通用的本地包更小且更易于维护。但这种效率来自于保持响应的动态性。网站不仅仅在开发期间下载固定包并从自己的基础设施部署经过审查的字节。它邀请另一个运营商在页面加载时决定访客将执行哪些字节。

这种区别决定了问责框架。开源仓库是可审查的代码和历史。托管服务是一种运营关系。实时服务依赖于域名、DNS、路由、托管、证书、部署访问以及能够更改响应的人员或组织。网站可以信任公共代码,但仍然未能检查为其访客提供服务的端点是否保持相同的控制、遵循相同的流程或返回相同类别的输出。

HTTPS 不能消除这些问题。它可以帮助浏览器验证其是否到达了所请求域名的有效授权持有者,并保护传输中的响应。但它不保证域名持有者不变,返回的程序是善意的,或者内容在请求之间是不变的。当域名控制权合法变更时,HTTPS 可以完全按设计继续工作,同时验证新的运营现实。

浏览器赋予远程加载的 JavaScript 对包含页面广泛的实际影响。具体影响范围取决于页面和浏览器控制,但基本弱点已经明确:第三方功能在第一方的 Web 上下文中执行。MITRE 的 CWE-830 将此类包含描述为信任转移到来自另一个域的代码,OWASP 将第三方 JavaScript 视为治理问题,因为它可能影响数据和页面行为。GitHub CodeQL 的指南将相同的逻辑应用于从不受信任的域加载的功能。[15][16][17]

这就是为什么该事件不应被简化为“开源变得不安全”。原始开源代码、经过审查的自托管副本和受域名控制的实时服务是不同的信任对象。使用固定本地副本的网站与请求cdn.polyfill.io变化的响应的网站所做的运行时委托不同。替代主机也创建了不同的运营关系,即使它提供的是源自同一项目的代码。

因此,第一个问责问题不是开发人员是否应该不信任每个外部库。而是网站在使用时是否知道哪一方可以为其访客选择可执行字节。一个有用的清单应至少回答五个问题:哪些页面请求了脚本,包含是直接还是传递的,哪些用户和浏览器路径可以访问它,什么业务功能需要它,以及谁目前控制着端点。

这些问题属于网站所有者,因为网站使包含生效。访客没有与 Polyfill.io 协商或选择其运营商。访客从网站请求页面,并合理地体验返回的脚本作为该页面的一部分。即使主题、插件、标签管理器或供应商插入了引用,下游组织仍然是向用户呈现综合体验的一方。

这并不意味着网站所有者是恶意代码的作者或消除了服务运营商的控制。它意味着委托的权限不能消除第一方责任。服务运营商可以选择响应。网站所有者可以选择该运营商是否保留在页面中的位置。这些是不同的控制,事件考验了这两者。

2 月的转让是一次软件治理事件

公开年表提供了一个异常重要的警告间隔。2024 年 2 月,Polyfill.io 域名及相关 GitHub 存在的控制权转移到 Funnull,如当时的消息来源所述。这些消息来源证实了控制和信任基础的变更。但仅凭这一事实并不能确定新运营商的动机、犯罪计划、法律违规或后来影响交付的每个人的最终身份。

对于传统的信息网站,域名转让主要改变发布权限。对于向其他网站返回可执行 JavaScript 的域名,转让改变了谁可以影响这些下游页面运行的软件。这使得所有权信息成为依赖状态的一部分。端点的新所有者在操作上相当于新的维护者、签名权限或发布渠道,即使源代码仓库看起来很熟悉。

Fastly 2 月 28 日的通知使治理重要性显而易见。它宣布了替代域名,并为用户提供了迁移、自托管或删除服务等选项。这些选项不仅仅是品牌替代方案。每一个都改变了谁将控制交付给访客的字节。迁移选择了不同的服务关系。自托管将交付置于网站自己的部署控制之下。删除则在不再需要时消除了兼容性依赖。[3]

Cloudflare 于 2 月 29 日跟进,提供了 cdnjs 托管的替代方案。其解释将供应商过渡与供应链风险联系起来:网站依赖另一方来维护和保护一个可能在其页面中执行代码的服务。Cloudflare 的替代方案并未使第三方托管无风险,但它表明基础设施提供商将所有权变更视为新信任决策的依据。[2]

2 月 28 日开出的 FormatJS 问题提供了同一时期的下游项目记录。它对所有权和 CNAME 关系提出了担忧,并要求项目停止推荐该端点。该记录很重要,因为它显示了一个维护者在公开报道的恶意活动之前就文档依赖采取了行动。删除推荐并不能修复之前遵循该推荐的每个网站,但它限制了未来的传播并创建了可追溯的警告。[4]

总之,这些记录防止了一种过于方便的叙述,即下游所有者直到 6 月 25 日才收到信号。并非每个网站所有者都会看到 Fastly 通知、Cloudflare 帖子或 FormatJS 问题。消息来源并未确凿证明普遍通知,将公开可用性转化为每个组织确实知道的证据是不公平的。但它们确实表明所有权变更是可观察的,替代方案是可用的,并且一些责任方在事件报告前几个月就重新评估了端点。

这种区别对问责至关重要。监控义务并不意味着全知。它意味着设计一个能够接收高级依赖项相关变更的流程。站点可能跟踪包公告,却忽略域名所有权、DNS 或托管变更。对于版本化包,发布和漏洞源可能是重要信号。对于远程脚本端点,注册、DNS、运营商和响应行为也应属于监控模型。

2 月的事件也暴露了一次性供应商审查的局限性。团队可能多年前基于运营商、基础设施、项目声誉和当时的浏览器需求批准了 Polyfill.io。如果批准记录只包含字符串cdn.polyfill.io,团队可能误认为只要 URL 不变,依赖项就保持不变。实际上,稳定名称背后的方已经改变。

负责任的审批应记录信任基础,而不仅仅是地址。该基础可包括运营商、服务目的、预期响应特性、合同或社区关系、可用的审计证据、回退计划和审查触发条件。所有权转让将使审批无效或至少重新审议。如果没有这样的记录,组织很难解释为什么在前提条件改变后继续委托仍然是合理的。

必要性问题也应重新讨论。Polyfill 与浏览器能力相关。浏览器群体演进,产品支持策略变化,曾经必要的兼容代码可能成为遗留。具有页面级别权限的远程依赖不应仅仅因为没有人负责删除而持续存在。2 月的通知提供了一个时机,询问旧浏览器支持是否仍然需要该端点,以及较小的本地包是否能满足剩余需求。

所有这些都不能证明保留引用的每个站点都有过失,也不意味着域名转让本身变成了攻击。它确立了一个更窄的观点:即使在观察到有害输出之前,转让就已经改变了重大的软件控制。网站问责始于该重大变更是否可检测和可审查。

选择性交付使随意检查不可靠

6 月 25 日,Sansec 报告称cdn.polyfill.io通过嵌入它的网站提供修改后的 JavaScript。观察到的行为将选定的访问者重定向到旨在模仿 Google Analytics 的域名,指向诈骗或博彩目的地。Sansec 描述的条件包括移动设备定位、服务器端和客户端检查、基于时间的行为,以及避开某些管理员或分析上下文。[1]

观察到的行为需要一个精确的名词。这是一个通过修改后的 JavaScript 交付的重定向活动。记录不支持将这一观察升级为凭据窃取、页面数据窃取、主机入侵、浏览器之外的代码执行或可量化的财务损失。原则上,远程控制的脚本端点可以返回能够实现更广泛浏览器操作的 JavaScript。CNCF TAG Security、CWE-830 和通用浏览器执行模型支持这一能力结论。能力并非每种可能操作都发生的证据。[8][16]

Cloudflare 表示其 Page Shield 数据证实了这些指标,并包括从 6 月 8 日开始的匹配。这是消息来源记录中描述的 Cloudflare 数据集中的最早匹配。这并不能证明所有恶意活动都从该日期开始,同一分支从那时起连续到达每个站点,或者在 Cloudflare 可见范围之外没有更早的交付。[5]

Akamai 分别描述了一个两阶段模式:基于请求头的服务器端选择决定发送什么,然后在重定向之前进行客户端检查。这种架构解释了为什么普通检查可能遗漏问题。从桌面浏览器请求脚本的开发人员可能收到预期的 polyfill。具有不同请求头、位置、时间或页面状态的移动访问者可能收到或激活另一个分支。[9]

选择性交付改变了证据负担。单个干净的下载不能证明历史安全性。从一个浏览器在一个时刻进行的源代码比较可能只显示为该请求选择的响应。搜索引擎爬虫、正常运行时间监控器或安全扫描器可能具有被交付逻辑故意排除的特征。管理员在登录时测试可能看到与首次访客不同的行为。

这并不意味着检测不可能。它意味着监控必须匹配依赖项的变异性。有用的观察应采样浏览器、设备、位置和请求条件;随时间保留响应体和哈希;检测新域名和重定向链;并将实际客户端观察到的行为与服务声明的目的进行比较。客户端监控发挥了重要作用,因为最终的程序在浏览器中汇编和执行。

该事件还说明了优化如何成为伪装。动态浏览器定位是 Polyfill.io 合法服务模型的一部分:该服务根据浏览器能力选择兼容代码。恶意分支可以利用响应自然不同的预期。变异性本身并不是滥用的证据。它使简单的“已知哈希等于安全服务”模型更难应用,并为选择性交付提供了在可接受的运营模式中隐藏的空间。

因此,问责取决于定义预期的变异性。网站应能说明提供商合法使用哪些请求属性,可能返回哪些代码类别,脚本可能联系哪些目标,以及哪些页面操作超出目的。没有这个基线,监控可以观察到变化,但不知道变化是否被授权。

服务器端采样也使回溯调查复杂化。站点的静态存储库可能只包含脚本 URL,而不是访问者收到的恶意字节。这些字节来自请求时的另一个系统,在遏制之后可能不再可用。浏览器遥测、CDN 日志、保存的响应、内容安全报告和端点记录可能是重构可达性的唯一证据。如果这些记录从未收集或保留时间过短,站点可能无法回答哪些访问者遇到了该分支。

这种不确定性不应通过宽泛的声称每个人都是安全的或每个人都被入侵来掩盖。负责任的声明可以定义发现了什么:端点被引用;特定条件路径可达或不可达;遥测覆盖了指定日期和群体;观察到的重定向匹配或不匹配已知指标;以及对保留证据之外的请求存在空白。

因此,选择性交付是责任测试的核心。它解释了有害输出如何与良性检查共存,为什么端点引用不是受害者数量,以及为什么经过验证的修复不仅仅需要在域名暂停后加载一次页面。

规模估计并非受害者数量

Sansec 描述了超过 100,000 个站点嵌入或使用该服务。Cloudflare 引用的估计表明 Polyfill.io 出现在接近 4% 的网站上。这些数字传达了广泛复用服务的潜在广度。它们没有相同的分母,两者都不能建立一套完整的已确认入侵。[1][5]

几个群体必须保持分离。一是当前或历史代码引用了 Polyfill.io 端点的网站集合。二是相关包含路径在生产中可达的网站集合。三是在活动期间请求了恶意响应的集合。四是其访客满足服务器端和客户端条件的集合。五是实际被重定向的访客。六是后来遭受可衡量损失的任何群体。

可用的公开记录没有为大多数这些群体提供经过验证的计数。因此,将每个引用称为受害者、每个包含的站点被称为被入侵或每个访客被暴露是不准确的。同样不准确的是简单地因为选择性交付阻止了普遍观察而将引用视为无害。引用建立了一个信任路径。需要额外的证据来确立可达性、交付和影响。

这种措辞对下游通知很重要。“我们的代码包含引用”不同于“我们的遥测显示脚本被请求”。两者都不同于“我们观察到了恶意分支”和“访客报告了重定向”。将它们合并可能造成不必要的警报或虚假安慰。分开它们可以让用户了解组织知道什么。

Wordfence 对受影响的 WordPress 插件模式的处理强化了这一边界。其目录识别了 Polyfill.io 的使用,但警告不要假设每个插件实例都传递了恶意内容。插件可能将端点引入许多站点,使清单工作紧迫,而代码的存在仍不足以证明在每个安装中的恶意执行。[14]

政府报告重复了严重的规模担忧,同时关注运营商的移除和调查。CERT-AGID 描述了收购和依赖请求头的交付,并提到了超过 100,000 的数字。这一独立警告支持广泛的防御关注,但不会将分母变为已确认受影响的访客。[21]

因此,最强烈的规模声明也是最有界限的:端点有大的下游足迹,观察到的选择性活动在该足迹上创造了风险。确切影响必须逐个站点和访客群体地确定。

遏制改变了路径;它并没有完成修复

响应涉及具有不同控制类型的各方。Cloudflare 自动将代理客户页面上的 Polyfill.io 引用重写为 Cloudflare 托管的镜像。该公司解释说,简单阻止原始域名可能会破坏仍然依赖该服务的站点。重写试图在消除对更改后端点的立即依赖的同时,保留预期的兼容性。[5]

这种干预说明了一个真实的运营权衡。安全团队通常倾向于立即移除可疑资源。产品团队知道突然消除兼容性层可能使某些访客无法使用站点。Cloudflare 在交付链中的位置使其可以替代不同来源,而无需等待每个网站所有者部署更改。

替代是遏制,而不是完全修复的证明。它改变了机制覆盖的流量的可信运营商和响应路径。它没有确定替代方案对每个浏览器具有相同的行为,所有页面和交付路径都被覆盖,或者下游存储库不再包含旧引用。它也没有回答在重写之前访客收到了什么。

Namecheap 随后将 Polyfill.io 域名置于暂停状态,政府公告称该端点已于 6 月 27 日暂停。域名级别的操作移除了即时的服务路径,但也可能破坏仍然期望响应的站点。暂停是注册商持有的重要遏制杠杆。它没有清理下游模板、插件设置、缓存页面、标签管理器配置或供应商产品。[6][10]

当域名不可用时,差异变得更加明显。站点可能看起来受到保护,因为浏览器无法再检索可疑脚本。然而,过时的引用仍然是未解决的依赖项。如果控制状态再次改变,如果替代主机名持续存在,或者内部副本未经审查,潜在的治理问题仍然存在。即使是永久的死端点也可能带来性能、错误处理和兼容性成本。

CERT-FR 和西澳大利亚网络安全部门建议运营商识别并删除引用,在需要时迁移到受控替代方案,并考虑子资源完整性和内容安全策略等浏览器控制。Semgrep 同样关注仓库范围的检测,而不是将域名暂停视为足够。[6][7][20]

这一序列暗示了四个不同的关闭声明。“已遏制”意味着已知的有害交付路径已被中断。“已移除”意味着下游引用和传递插入路径已消失。“已调查”意味着利用可用证据评估了历史范围和影响。“已验证”意味着测试和监控表明当前站点不再依赖旧路径,且替代方案在其预期范围内运行。

网站所有者可以依赖基础设施提供商来满足第一个声明,但仍需负责其他三个。Cloudflare 可以重写它看到的流量。Namecheap 可以暂停它注册的域名。两者都不知道客户嵌入 URL 的每个位置、插件内的每个条件浏览器路径或网站需要调查的每个访客群体。

这种分离也防止夸大第三方干预。注册商暂停不是归因发现。自动重写并不证明每个受保护的客户都提供了恶意分支。检测供应商的指示器匹配不是每个站点的完整取证报告。每个行动应根据其行使的控制和产生的证据来描述。

在操作上,下游补救应以完整搜索开始。字面主机名可能出现在源文件、生成的包、内容管理字段、插件代码、标签管理器、模板、存档配置或供应商响应中。搜索工具可以找到已知字符串,但依赖关系清单还必须解释谁引入了引用以及哪个构建或运行时路径使其生效。

然后,移除需要一个功能决策。如果 polyfill 不再必要,消除它是最干净的权限缩减。如果仍然需要旧版浏览器支持,经过审查的本地包或明确批准的提供商可能是合适的。不应仅因为其 URL 方便就选择替代方案。其运营商、更新过程、响应可变性和监控模型成为新信任基础的一部分。

最后,历史评估需要与站点风险相称的证据。浏览器端观察、网络日志、安全报告、客户投诉和保存的脚本响应可能有助于确定已知的重定向指示器是否出现。证据缺失应与覆盖范围相关联。没有保留客户端遥测的站点可以说它在可用记录中没有发现报告或指示器;它不能将缺失的记录转化为没有访客收到该分支的证明。

Fides 展示了为什么条件分支仍然重要

CVE-2024-38537 记录了 Fides 中的一个下游问题。相关的fides.js路径可能为旧版浏览器加载 Polyfill.io。该记录标识了受影响版本,并表示 2.39.1 版本移除了暴露。它还保留了一个重要边界:尚未发现通过 Fides 的利用。[11][12]

这个案例很有用,因为它抵制了两种相反的错误。第一种会忽视该问题,因为只有旧版浏览器路径加载了端点。条件可达性仍然是可达性。如果生产页面可以为受支持的访客群体请求远程 JavaScript,则该依赖项应属于产品的安全清单,即使大多数开发者从未触发它。

第二种错误会将 CVE 视为 Fides 用户被利用的证据。漏洞记录可以确立不安全的包含路径和受影响版本,但并不证明恶意分支到达了特定部署。“可能加载”和“被观察到利用”回答了不同的问题。Fides 记录明确保持了这一区别。

修复也说明了为什么版本化修复很重要。在命名版本中移除远程依赖为下游操作员提供了具体的行动和可追溯的边界。他们可以识别已部署的版本、升级、扫描剩余引用并测试旧版路径。一般的警告“对 Polyfill.io 保持警惕”不会提供相同的关闭证据。

旧版条件应影响验证。仅测试现代桌面浏览器可能永远不会执行受影响的分支。验证应重现或检查最初选择它的条件。这可能需要审查打包代码、模拟较旧的用户代理、检查网络请求并确认修复版本不再构建或请求端点。

SingCERT 的公报也讨论了下游 Fides 案例,并保留了未观察到利用的边界。国家 CERT 的重复增加了问题的可见性;但并未将可能性转化为观察到的利用。[22]

因此,Fides 的教训不是每个条件性的第三方请求都应有相同的严重性。而是维护者应了解条件、受影响版本、可达人群、修复版本和证据边界。当正式漏洞记录既说明了可能发生的情况也说明了未观察到的情况时,问责最强。

Jellyfish 展示了如何追踪传递依赖

Jellyfish 的安全建议记录了一条不同的路径。其服务依赖于一个在特殊条件下可能加载 Polyfill.io 的供应商。Jellyfish 识别了传递关系,联系了供应商,验证了移除,并限定了可能到达该路径的浏览器群体。[13]

这个序列是一个实用的模型,因为它从架构开始而不是指责。面向客户的组织可能没有直接在其自己的存储库中放置脚本标签。引用可能来自分析组件、同意工具、插件、支持小部件或其他供应商的代码。对行的直接所有权并不等同于对用户体验的控制。

传递依赖带来了证据问题。面向构建时安装包的软件物料清单可能不包含供应商在浏览器中动态请求的域名。合同清单可能命名供应商,但不包括供应商自己的脚本供应商。网络监控可能看到主机名,但未识别引入它的业务所有者。需要所有三个视图才能将请求连接到负责的关系。

Jellyfish 的响应序列解决了这些空白。第一,识别依赖存在及可以调用它的条件。第二,将其映射到供应商关系。第三,要求具有代码控制权的一方移除它。第四,验证更改,而不是将供应商的保证视为事件的结束。第五,使用最佳可用浏览器和产品证据限定人群。

当条件不常见时,验证尤其重要。供应商可能移除可见的引用,而回退、旧包或缓存资产仍然包含它。客户应从外部测试,并接受内部确认。跨相关浏览器的网络捕获、存储库或包扫描以及客户端监控可以显示请求是否实际消失了。

限定的人群应保留其分母。如果只有某些浏览器版本或页面流可以触发供应商路径,调查可以缩小潜在范围。不应暗示该浏览器组的每个成员都收到了恶意内容。相反,路径罕见的事实并不意味着可以不去除它。罕见路径通常接受较少的常规测试,这可能使它们成为依赖关系持续未被注意的具有吸引力的地方。

Jellyfish 也展示了共享但不可互换的责任。供应商控制其代码,可以移除包含。Jellyfish 控制供应商升级、面向客户的调查和修复的接受。基础设施和安全提供商可以贡献遥测。这些方都不能完全替代另一方。

该模型可扩展到本事件之外。下游组织需要为任何能够引入可执行代码的供应商提供升级路径。合同或技术入职流程应确定谁可以回答紧急依赖问题、第三方脚本可以多快被禁用、哪些日志可用以及客户如何验证更改。

WordPress 插件展示了为什么引用和利用必须保持分离

Wordfence 将 Polyfill.io 在多个 WordPress 插件模式中的使用编目。这种清单很有价值,因为插件可以将一个外部依赖分发到许多独立运营的网站。一个小维护者的决定可能变成广泛的下游信任关系,而每个站点所有者并没有有意识地添加该端点。[14]

该目录也带有一个关键警告:端点的使用并不证明每个插件或站点都交付了恶意内容。引用识别了一个潜在的执行路径。该路径是否活跃取决于插件版本、配置、页面渲染、缓存、浏览器条件和当时的远程响应。

对于 WordPress 站点所有者,正确的反应不是争论插件作者或服务运营商是否是“真正的”责任方。立即的任务是本地的:识别已安装和活跃的版本,确定哪些页面渲染了引用,更新或移除受影响的组件,必要时清除生成的资产,并验证公共站点的网络行为。

插件维护者有一组不同的任务。它可以移除依赖、发布修复版本、解释受影响条件、更新文档和通知用户。插件存储库或安全服务可以分发警告。网站所有者仍必须部署更改。已安装的修复版本未安装不会改变浏览器路径。

这是为什么规模数字不应被视为受损组织数量的另一个原因。一个插件可以创建数千个引用;一个站点可以包含几个具有相同主机名的插件;休眠或禁用的插件可以留在磁盘上而不渲染脚本;缓存的公共页面可以在源代码更改后继续提供旧的引用。对字符串、安装数、活跃请求和恶意交付的计数会产生不同的数字。

一个负责任的生态系统保持这些度量有标签。安全情报可以发布广泛的暴露列表以加速行动。维护者可以说明受影响版本。站点所有者可以报告已部署的可达性。事件调查者可以报告观察到的指示器。没有人应从其他人那里借用确定性。

SRI 和 CSP 是控制手段,不是神奇答案

子资源完整性 (SRI) 允许页面作者为外部资源提供加密摘要。支持的浏览器可以获取资源,如果返回的字节与预期摘要不匹配,则拒绝执行。W3C 的规范和 MDN 的实现指南将 SRI 介绍为一种防止受损的第三方主机静默更改包含站点期望保持固定的资源的方式。[18][19]

对于稳定的脚本来说,这是一个强有力的控制。它将开放式的委托转化为对特定字节的批准。如果主机返回任何其他内容,浏览器会阻止执行。然后,网站可以在审查新版本后通过其自己的部署过程更新摘要。

Polyfill.io 的合法设计使该模型复杂化,因为该服务故意根据浏览器能力和请求参数生成不同的包。一个稳定的摘要无法批准许多有效的字节序列,除非网站改变其消耗该服务的方式。团队可以在某些架构中预计算并批准一组有界的固定资源,但将一个哈希附加到其目的是动态响应选择的端点上可能会破坏预期行为或留下重要的变异不受约束。

正确的结论不是 SRI 无用。而是控制选择必须匹配资源模型。如果站点想要完整性固定,它可能需要停止请求远程服务生成任意特定于请求的字节。自托管审查过的包、提供固定版本的变体或缩小支持的浏览器范围可以使字节级批准变得实用。

内容安全策略 (CSP) 处理不同的层次。策略可以限制哪些来源可以提供脚本,并可以使用 nonces、哈希或相关指令来约束执行。它可以阻止意外域并减少注入标记加载新资源的自由度。但如果在白名单中明确允许cdn.polyfill.io作为可信脚本来源,那么一个经来源批准的恶意响应并不会因为在允许列表上而变得可信。

CSP 仍然可以帮助遏制次要行为。精心设计的策略可以限制攻击链中涉及的连接、框架或导航,违规报告可以增加检测证据。确切效果取决于策略和浏览器行为。核心限制仍然存在:来源批准回答了代码可能来自哪里,而不是批准的运营商是否会始终返回可接受的代码。

镜像再次移动了信任边界。网站或基础设施提供商获取或维护副本,并从受控位置提供服务。这可以防止原始域名在请求时更改字节。它也为镜像的获取、审查、更新和保护创造了义务。自动重写为 Cloudflare 的镜像是有用的遏制,但它选择了 Cloudflare 作为新的运营权威;它并没有消除信任的概念。

自托管使网站所有者对交付有更直接的控制。它可以审查版本,随应用程序部署它,并通过正常的发布流程监控更改。自托管不保证安全的代码。它缩小了谁可以更改生产响应,并使部署的字节更容易绑定到发布。

当功能不再必要时,移除更强大。如果当前的浏览器支持不再需要 polyfill 服务,风险最低的远程脚本是页面不请求的那个。这就是为什么生命周期审查应伴随安全控制。多年前做出的兼容性决策不应成为永久的权限授予。

沙盒可以在功能在受限框架或隔离上下文中运行时减少一些第三方影响。并非每个脚本都可以在不更改产品的情况下移动到那里。旨在修改页面 JavaScript 环境的 polyfill 特别依赖于主执行上下文,这限制了隔离的有用性。该限制应影响便利性是否仍然值得权限。

代码扫描工具有助于找到已知引用。CodeQL 针对 Polyfill 的指南强调了所有权尽职调查、日志审查、自托管以及完整性控制对动态内容的局限性。Semgrep 在事件后提出了仓库搜索和规则来识别 Polyfill.io 的使用。[15][20]

仅静态扫描是不完整的,因为运行时注入可能来自内容系统、标签管理器或供应商。仅运行时观察是不完整的,因为罕见条件在采样期间可能不会发生。成熟的控制栈结合了源代码和包扫描、第三方脚本清单、DNS 和所有权监控、浏览器端遥测、变更审查和紧急禁用机制。

控制栈还应指定故障行为。如果 polyfill 加载失败,页面是丢失一个次要增强、变得不可用还是阻止关键交易?了解故障影响的团队可以快速移除或阻止可疑资源,而无需在事件期间临时应对。如果连续性依赖于该资源,经过测试的本地回退比在注册商暂停域名时发现依赖更安全。

因此,控制不是一个可以从中选择一个时尚缩写的菜单。它们是一系列决策:移除不必要的权限,尽可能使必要的代码固定,限制它从哪里来,观察它做什么,保留证据,并维护一个经过测试的路径来禁用或替换它。

问责应遵循每一方持有的控制

该事件涉及服务运营商、网站所有者、基础设施提供商、注册商、安全研究人员、插件和产品维护者以及代码引入端点的供应商。他们的责任有重叠,但不可互换。

控制托管 Polyfill.io 服务的运营商对该服务返回的响应拥有最直接的权威。这与维护原始开源代码不同。这一层的问责涉及域名和部署路径的保管、变更控制、响应完整性、交付可见性以及关于运营的准确沟通。可用的记录不应被扩展为关于每个个体行为者、公司关系或法律义务的发现。

Fastly 和 Cloudflare 拥有基础设施和替代能力。他们 2 月的通知可以警告并提供替代方案。Cloudflare 后来的位置允许对覆盖的流量进行自动重写和客户端遥测。这些控制很重要,但并没有让任何一方完全了解每个下游站点的来源、配置或访客影响。[2][3][5]

Namecheap 持有注册商级别的遏制杠杆。暂停域名中断了端点的解析或使用。该行动减少了立即暴露,同时可能破坏依赖的站点。注册商控制可以停止路径;但不能修补应用程序或为每个网站建立历史交付。[10]

安全研究人员和政府响应者拥有检测、分析和警告能力。Sansec 发布了指示器和观察到的行为。Akamai 和 Cloudflare 添加了遥测视角。CERT-FR、西澳大利亚、CERT-AGID 和 SingCERT 将该事件转化为针对其受众的操作指南。这些方可以提高可见性并推荐控制;他们不能在每个站点上部署修复。[1][5][6][7][9][21][22]

插件、库和供应商维护者控制可能传递引入依赖的代码。他们负责任的行动包括识别受影响的版本和条件、移除端点、发布修复、沟通范围以及保持潜在暴露和观察到的利用之间的区别。Fides 和 WordPress 插件记录说明了为什么版本和可达性证据很重要。[11][12][14]

网站所有者控制了最终的包含决定,即使在执行时需要供应商或维护者更改代码。他们可以定义支持的浏览器、批准第三方脚本提供商、维护清单、监控所有权、阻止或重写资源、自托管审查过的代码、保留浏览器证据、调查投诉以及与访客沟通。

这并不意味着同等责任。小型站点运营商的可见性和专业知识可能远低于全球基础设施公司。供应商可能是唯一能够更改捆绑回退的一方。注册商可能是唯一能够快速暂停域名的一方。问责遵循实际控制和可用的证据来行使它,而不是假设每个参与者都有相同的能力。

一个有用的职责地图可以围绕六个问题组织。

第一,谁可以防止不必要的暴露?网站和产品所有者可以重新审视浏览器支持并移除依赖。维护者可以停止推荐或捆绑它。提供商可以提供更安全的迁移路径。

第二,谁可以检测到信任变化?域名和基础设施监控者可以观察所有权、DNS 和路由变化。项目维护者可以跟踪账户控制和文档。网站所有者可以订阅相关通知,并在其运营商变更时审查高权威依赖项。

第三,谁可以观察到有害交付?服务运营商和基础设施提供商可以看到服务器端响应。网站所有者和客户端安全服务可以看到浏览器行为。研究人员可以跨条件比较样本。没有任何单一视图必然覆盖整个活动。

第四,谁可以遏制路径?运营商可以停止交付,注册商可以暂停域名,基础设施提供商可以重写或阻止流量,维护者可以发布修复,网站所有者可以禁用或移除引用。

第五,谁可以调查影响?每个网站所有者拥有自己的页面架构、访客记录、投诉和部署历史。供应商掌握关于传递条件的信息。基础设施提供商拥有选定的流量和响应遥测。调查需要合作,而不假设一方的数据集代表所有访客。

第六,谁可以验证修复并沟通?维护者可以将修复绑定到版本。供应商可以展示移除。网站所有者可以测试公共路径并说明他们的证据覆盖了什么。政府和安全机构可以更新指南。验证必须保持在该方的可见性范围内。

这个地图使问责可测试。它不仅仅询问谁导致了事件,而是询问哪一方可以回答每个预防、检测、遏制、调查和修复问题。它也暴露了控制缺口。如果没有人监控具有页面级别权限的脚本的域名所有权,那么缺口在恶意分支出现之前就存在了。

下游网站应能证明什么

一个可信的下游响应可以表示为证据链,而不是通用保证。

链从清单开始。组织应识别每个直接和传递的 Polyfill.io 引用、引入它的组件或供应商、渲染它的页面以及使其可达的浏览器条件。搜索结果应连接到部署的行为,而不是作为匹配文件的列表留下。

接下来是必要性。所有者应记录兼容性功能是否仍然对其有意支持的浏览器必要。如果不是,应优先移除。如果是,组织应解释为什么选择的替代方案或自托管版本与需求相称。

第三步是信任重新评估。记录应显示组织何时了解到所有权转让或 6 月事件,谁做出了继续、阻止、替换或移除服务的决定,以及什么证据支持该决定。2 月的审查和 6 月的紧急响应是不同的事件,不应合并。

第四步是历史范围。组织应说明其检查了哪些日期、访客群体和遥测来源。应区分端点引用、请求、已知指示器匹配、重定向和报告的有害行为。如果日志缺失,缺口应保持明确。

第五步是修复。所有者应将更改绑定到发布、配置或供应商确认。应考虑生成的资产、缓存、插件、标签管理器和条件分支。仅仅观察到暂停的域名不再响应不是移除证据。

第六步是验证。测试应覆盖相关的浏览器条件,并确认公共页面不对旧端点发出请求。监控应寻找重新引入、意外的脚本来源和重定向行为。在供应商提供修复的情况下,客户应验证外部结果。

第七步是沟通。通知应使用稳定的定义,避免将使用转化为受害者身份。应说明存在什么依赖,是否可达,发现了或未发现什么恶意交付的证据,什么改变了以及哪些不确定性仍然存在。用户需要实际事实,而不是一个广泛声明说问题“已解决”。

最后,所有者应更新其生命周期控制。高权限的远程脚本需要指定的所有者、审查日期、信任基础记录、域名和运营商监控、紧急禁用路径以及有用浏览器证据的保留。否则,同样的治理失败可能在不同的主机名下再次发生。

这些证明不需要披露敏感的防御细节。它们需要足够的特异性使响应可证伪。访客、客户或审查者应能区分“我们删除了字符串”和“我们找到了每个活跃路径”,以及“我们未观察到利用”和“我们的证据排除了覆盖人群的可能性”。

域名转让可以是一种软件变更

Polyfill.io 使一个简单的原则难以忽视:当域名返回可执行代码时,域名的所有权是软件安全状态的一部分。URL 可以保持稳定,而有效供应商变更。仓库可以保持公开,而托管响应转移到不同的控制之下。HTTPS 可以保持有效,而证明包含合理性的信任基础不再存在。

2 月的通知表明这一变化是可见且可操作的。6 月的报告表明了为什么它很重要。选择性重定向交付利用了不同访客可以合法接收不同代码的模型,使随意检查成为薄弱的保证。后来的重写和域名暂停制约了路径,但只有下游的清单、移除、调查和验证才能关闭每个网站的问题部分。

该事件不能证明超过 100,000 个站点是确认的受害者。它不能证明每个引用都提供了恶意内容,每个访客都被暴露,或每个下游产品都被利用。它也不能证明将原始开源代码描述为普遍恶意,或将自托管副本视为与受损害的托管关系相同。

该事件也不支持从现有记录中得出法律判决或完整的归因故事。此处的问责更狭窄且更具操作性。它是解释为什么可执行权限被委托、如何检测到控制变更、什么证据确立了可达性、哪个行动减少了暴露以及修复如何被验证的义务。

服务运营商、基础设施公司、注册商、维护者、供应商和网站所有者各自持有该答案的不同部分。责任是共享的,因为系统是共享的,但不是可互换的。注册商可以暂停域名,但仍留下过时的代码。供应商可以移除引用,但仍缺乏客户的访客遥测。网站可以调查其用户,但仍依赖上游方更改捆绑的组件。

对于下游所有者,持久的标准是直接的:了解每个可以为你的访客选择代码的外部方;监控使该方值得信赖的事实;移除不再服务于必要功能的权限;并保留足够的证据来区分暴露、交付和伤害。

一个脚本标签可能只是一行 HTML,但它创建了一个运营关系。当该行背后的所有者改变时,软件关系也随之改变。网站问责始于在访客不得不揭示它之前认识到这一变化。

来源

  1. https://sansec.io/research/polyfill-supply-chain-attack
  2. https://blog.cloudflare.com/polyfill-io-now-available-on-cdnjs-reduce-your-supply-chain-risk/
  3. https://community.fastly.com/t/new-options-for-polyfill-io-users/2540
  4. https://github.com/formatjs/formatjs/issues/4363
  5. https://blog.cloudflare.com/automatically-replacing-polyfill-io-links-with-cloudflares-mirror-for-a-safer-internet/
  6. https://cert.ssi.gouv.fr/actualite/CERTFR-2024-ACT-030/
  7. https://soc.cyber.wa.gov.au/advisories/20240626004-JavaScript-Polyfill-Supply-Chain-Attack/
  8. https://tag-security.cncf.io/community/catalog/compromises/2024/polyfill/
  9. https://www.akamai.com/blog/security/polyfill-supply-chain-attack-what-to-know
  10. https://socket.dev/blog/namecheap-takes-down-polyfill-io-service-following-supply-chain-attack
  11. https://www.cve.org/CVERecord?id=CVE-2024-38537
  12. https://nvd.nist.gov/vuln/detail/cve-2024-38537
  13. https://jellyfish.co/library/jellyfish-security-advisory-june-27-2024/
  14. https://www.wordfence.com/threat-intel/vulnerabilities/detail/various-plugins-various-version-use-of-polyfillio
  15. https://codeql.github.com/codeql-query-help/javascript/js-functionality-from-untrusted-domain/
  16. https://cwe.mitre.org/data/definitions/830.html
  17. https://cheatsheetseries.owasp.org/cheatsheets/Third_Party_Javascript_Management_Cheat_Sheet.html
  18. https://www.w3.org/TR/SRI/
  19. https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/SRI
  20. https://semgrep.dev/blog/2024/protect-your-code-from-the-polyfill-supply-chain-attack/
  21. https://cert-agid.gov.it/news/scoperto-un-grave-attacco-alla-supply-chain-del-servizio-polyfill-io-piu-di-100-000-i-siti-coinvolti/
  22. https://isomer-user-content.by.gov.sg/36/8bee5efc-3166-44a1-89ae-0f0b095ecb17/03-July-2024.pdf