摘要
- 加载
cdn.polyfill.io的网站不仅仅是引用了一个开源项目,而是允许一个实时远程服务选择 JavaScript 并在网站浏览器上下文中返回执行。因此,信任对象包括域名、运营者、路由和响应路径,而不仅仅是可在其他地方审查的源代码。[15][16][17] - Polyfill.io 域名及相关项目控制权在 2024 年 2 月发生变更。Fastly、Cloudflare 以及一个 FormatJS 项目问题当时做出了反应,表明在 6 月公开报告恶意交付之前,下游重新评估是可能的。权属转移本身不应被描述为恶意意图的证据。[2][3][4]
- Sansec 于 6 月 25 日报告称,该服务有选择地返回修改后的 JavaScript,将符合条件的访客重定向。Cloudflare 表示 Page Shield 指标包括最早从 6 月 8 日开始匹配的数据,而 Akamai 描述了服务器端采样后客户端检查的模式。这些观察结果确认了一个重定向活动,但并不意味着远程提供 JavaScript 的每一操作理论上都能执行。[1][5][9]
- Sansec 对超过 10 万个网站的估计指的是嵌入或使用该服务的网站。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 协商或选择其运营者。访客从网站请求页面,并合理地体验返回的脚本作为该页面的一部分。即使主题、插件、标签管理器或供应商插入了引用,下游组织仍然是向用户呈现组合体验的方。
这并不使网站所有者成为恶意代码的作者,也不消除服务运营者的控制。这意味着委托的权力并不消除第一方的责任。服务运营者可以选择响应。网站所有者可以选择该运营者是否保留在页面中的位置。这些是不同的控制,此事件测试了这两者。
二月的转移是一次软件治理事件
公开的时间线提供了一个异常重要的预警间隔。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、运营者和响应行为也属于监控模型。
二月的事件也揭示了一次性供应商审查的局限性。团队可能多年前基于运营者、基础设施、项目声誉和当时浏览器需求批准了 Polyfill.io。如果批准记录仅包含字符串cdn.polyfill.io,团队可能错误地将依赖视为不变,只要 URL 不变。实际上,稳定名称背后的方已经改变。
负责任的批准应记录信任基础,而不仅仅是地址。该基础可能包括运营者、服务目的、预期响应特征、合同或社区关系、可用审计证据、回退计划和审查触发条件。所有权转移随后将使批准失效或至少重新开放。没有这样的记录,组织很难解释为什么在前提变更后持续委托仍然是合理的。
必要性问题也应被重新审视。Polyfill 与浏览器能力相关。浏览器群体演变,产品支持策略变化,曾几何时必不可少的兼容性代码可能变得多余。具有页面级权限的远程依赖不应仅仅因为无人负责移除而持续存在。二月的通知提供了一个机会来询问旧浏览器支持是否仍需要该端点,以及较小的本地包是否能满足剩余需求。
这些都不证明保留引用的每个网站都有疏忽,也不将域名转移本身变成攻击。它确立了一个更狭窄的观点:转移在有害输出被观察到之前就改变了重要的软件控制。网站责任始于这个实质性变更是否可检测和可审查。
选择性传递使随意检查不可靠
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 描述了超过 10 万个网站嵌入或使用该服务。Cloudflare 引用估计 Polyfill.io 出现在近 4%的网站上。这些数字传达了一个广泛复用的服务的潜在广度。它们不共享相同的分母,并且两者都没有确定一组已确认的受损网站。[1][5]
几个群体必须保持分离。一组是当前或历史代码引用了 Polyfill.io 端点的网站。另一组是相关包含路径在生产中可达的网站。第三组是在活动期间请求了恶意响应的网站。第四组是访客满足服务器端和客户端条件的网站。第五组是实际被重定向的访客。第六组是任何后来遭受可测量损失的群体。
可用的公开记录并未为大多数这些群体提供经过验证的数字。因此,将每个引用称为受害者、每个包含网站称为受损或每个访客称为暴露是不准确的。同样不准确的是将引用视为无害,仅仅因为选择性传递阻止了普遍观察。引用建立了信任路径。需要额外证据来确定可达性、传递和影响。
这种词汇对于下游通知很重要。“我们的代码包含一个引用”不同于“我们的遥测显示脚本被请求”。两者都不同于“我们观察到了恶意分支”和“访客报告了重定向”。将它们合并可能造成不必要的恐慌或虚假安慰。区分它们让用户了解组织知道什么。
Wordfence 对受影响 WordPress 插件模式的处理强化了这一界限。其目录识别了 Polyfill.io 的使用,但警告不要假设每个插件实例都提供了恶意内容。插件可能将端点引入许多网站,使得盘点工作紧迫,而代码的存在仍然不足以证明每个安装中发生了恶意执行。[14]
政府报告重复了严重的规模担忧,同时将运营者焦点放在移除和调查上。CERT-AGID 描述了获取和头部依赖传递,并引用了超过 10 万这个数字。该独立警告支持广泛的防御关注,但它并不将分母变为确认的受影响访客。[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)处理不同的层次。策略可以限制哪些源可以提供脚本,并使用 nonce、哈希或相关指令约束执行。它可以阻止意外域名并减少注入标记加载新资源的自由。但如果cdn.polyfill.io被明确允许作为受信任的脚本源,源批准的恶意响应并不会因为出现在允许列表中而变得值得信赖。
CSP 仍然有助于遏制次级行为。精心设计的策略可以限制攻击链中涉及的连接、框架或导航,违规报告可以增加检测证据。确切效果取决于策略和浏览器行为。核心限制仍然存在:源批准回答代码可能来自哪里,而不是批准的运营者将始终返回可接受的代码。
镜像再次移动信任边界。网站或基础设施提供商获取或维护一个副本,并从受控位置提供它。这可以防止原始域名在请求时更改字节。它也为镜像如何获取、审查、更新和保护创造了义务。自动重写为 Cloudflare 的镜像是有用的遏制,但它选择了 Cloudflare 作为新的运营权限;它没有消除信任的概念。
自托管给予网站所有者对交付更直接的控制。它可以审查版本,与应用程序一起部署,并通过其正常发布过程监控更改。自托管不保证安全代码。它缩小了谁可以更改生产响应,并使部署的字节更容易绑定到发布。
当功能不必要时,移除更强大。如果当前浏览器支持不再需要 polyfill 服务,风险最低的远程脚本是页面不请求的脚本。这就是为什么生命周期审查应与安全控制并列。多年前做出的兼容性决策不应成为永久权限授予。
沙盒可以在功能可以在受约束的框架或隔离上下文中运行时减少一些第三方影响。并非每个脚本都可以在不更改产品的情况下移动到那里。旨在修改页面 JavaScript 环境的 polyfill 特别依赖于主执行上下文,这限制了隔离的有用性。这种限制应影响便利性是否仍值得权限的权衡。
代码扫描工具有助于找到已知引用。CodeQL 针对 Polyfill 的指南强调所有权尽职调查、日志审查、自托管和针对动态内容的完整性控制的局限性。Semgrep 提出了仓库搜索和规则来在事件发生后识别 Polyfill.io 的使用。[15][20]
仅静态扫描是不完整的,因为运行时注入可能来自内容系统、标签管理器或供应商。仅运行时观察是不完整的,因为罕见条件可能不会在样本期间发生。成熟的控制栈结合了源和包扫描、第三方脚本清单、DNS 和所有权监控、浏览器端遥测、变更审查和紧急禁用机制。
栈还应指定故障行为。如果 polyfill 加载失败,页面是否会丢失次要增强、变得不可用或阻止关键交易?理解失败影响的团队可以快速移除或阻止可疑资源,而无需在事件期间临时应对。如果连续性依赖于资源,经过测试的本地回退比发现依赖在注册商暂停域名时更安全。
因此,控制不是可以从其中选择一个时尚缩写的菜单。它们是一个决策序列:移除不必要的权限,尽可能使必要的代码固定,约束它可能从哪里来,观察它做什么,保留证据,并维护一个经过测试的路径来禁用或替换它。
责任应遵循各方持有的控制权
此事件涉及服务运营者、网站所有者、基础设施提供商、注册商、安全研究人员、插件和产品维护者,以及其代码引入端点的供应商。他们的责任重叠,但不可互换。
控制托管的 Polyfill.io 服务的运营者对该服务返回的响应拥有最直接的权限。这与维护原始开源代码不同。这一层的责任涉及域名的保管和部署路径、变更控制、响应完整性、交付可见性以及关于运营的准确沟通。可用记录不应被拉伸为关于每个个体参与者、公司关系或法律义务的结论。
Fastly 和 Cloudflare 拥有基础设施和替代能力。它们的二月公告可以发出警告并提供替代方案。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 引用、引入它的组件或供应商、渲染它的页面以及使其可达的浏览器条件。搜索结果应与部署行为相关联,而不是留作匹配文件的列表。
接下来是必要性。所有者应记录兼容性功能是否仍然对它有意识支持的浏览器是必需的。如果不是,应优先移除。如果是必需的,组织应解释为什么选择的替代或自托管版本与需求相称。
第三步是信任重新评估。记录应显示组织何时了解所有权过渡或六月事件,谁做出了继续、阻止、替换或移除服务的决定,以及支持该决定的证据。二月的审查和六月的应急响应是不同的活动,不应合并。
第四步是历史范围。组织应说明检查了哪些日期、访客群体和遥测源。它应分离端点引用、请求、已知指标匹配、重定向和报告的损害。如果日志缺失,空白应保持明确。
第五步是修复。所有者应将更改绑定到发布、配置或供应商确认。它应考虑到生成的资产、缓存、插件、标签管理器和条件分支。仅仅观察暂停的域名不再响应不是移除的证据。
第六步是验证。测试应覆盖相关浏览器条件,并确认公共页面未向旧端点发出任何请求。监控应寻找重新引入、意外的脚本源和重定向行为。如果供应商提供了修复,客户应验证外部结果。
第七步是沟通。通知应使用稳定的定义,避免将使用转化为受害。它应解释存在什么依赖,是否可达,找到了或未找到什么恶意交付证据,什么改变了,以及哪些不确定性仍然存在。用户需要实用的事实,而不是“已解决”的广泛声明。
最后,所有者应更新其生命周期控制。高权限远程脚本需要指定的所有者、审查日期、信任基础记录、域名和运营者监控、紧急禁用路径以及有用浏览器证据的保留。否则,相同的治理失败可以在另一个主机名下重复发生。
这些证明不需要泄露敏感防御细节。它们需要足够的特异性使响应可证伪。访客、客户或审查者应能区分“我们移除了字符串”与“我们找到了每个活动路径”,以及“我们未看到利用”与“我们的证据在覆盖群体中排除了它”。
域名转移可以是软件变更
Polyfill.io 使一个简单原则难以忽视:当域名返回可执行代码时,域名的所有权是软件安全状态的一部分。URL 可以保持稳定而有效供应商改变。仓库可以保持公开而托管响应在不同控制下移动。HTTPS 可以保持有效而证明包含合理的信任基础不再存在。
二月的通知显示了这一变化是可见且可操作的。六月的报告显示了为什么它重要。选择性重定向交付利用了一个模型,其中不同访客可以合法接收不同代码,使随意检查成为弱保证。后来的重写和域名暂停约束了路径,但只有下游盘点、移除、调查和验证才能关闭每个网站的部分问题。
此事件并不证明超过 10 万个网站是确认的受害者。它不证明每个引用都提供了恶意内容,每个访客都被暴露,或每个下游产品被利用。它也不证明将原始开源代码描述为普遍恶意,或将自托管副本视为等同于受损的托管关系。
事件也不支持从可用记录中得出的法律判断或完整归因故事。这里的责任更窄且更具操作性。它是解释为什么可执行权限被委托、变化控制如何被检测、什么证据确立了可达性、什么行动减少了暴露以及修复如何被验证的义务。
服务运营者、基础设施公司、注册商、维护者、供应商和网站所有者各自拥有该答案的不同部分。责任是共享的,因为系统是共享的,但它不可互换。注册商可以暂停域名,但仍留下陈旧的代码。供应商可以移除引用,但仍缺乏客户的访客遥测。网站可以调查其用户,但仍依赖上游方更改捆绑组件。
对于下游所有者,持久标准是直接的:知道每个能为您的访客选择代码的外部方;监控使该方值得信赖的事实;移除不再服务于必要功能的权限;并保留足够的证据来区分暴露、传递和损害。
一个脚本标签可能只是一行 HTML,但它创建了一种运营关系。当该行之后的所有者改变时,软件关系也随之改变。网站责任始于在访客不得不揭示它之前识别这一变化。
来源
- https://sansec.io/research/polyfill-supply-chain-attack
- https://blog.cloudflare.com/polyfill-io-now-available-on-cdnjs-reduce-your-supply-chain-risk/
- https://community.fastly.com/t/new-options-for-polyfill-io-users/2540
- https://github.com/formatjs/formatjs/issues/4363
- https://blog.cloudflare.com/automatically-replacing-polyfill-io-links-with-cloudflares-mirror-for-a-safer-internet/
- https://cert.ssi.gouv.fr/actualite/CERTFR-2024-ACT-030/
- https://soc.cyber.wa.gov.au/advisories/20240626004-JavaScript-Polyfill-Supply-Chain-Attack/
- https://tag-security.cncf.io/community/catalog/compromises/2024/polyfill/
- https://www.akamai.com/blog/security/polyfill-supply-chain-attack-what-to-know
- https://socket.dev/blog/namecheap-takes-down-polyfill-io-service-following-supply-chain-attack
- https://www.cve.org/CVERecord?id=CVE-2024-38537
- https://nvd.nist.gov/vuln/detail/cve-2024-38537
- https://jellyfish.co/library/jellyfish-security-advisory-june-27-2024/
- https://www.wordfence.com/threat-intel/vulnerabilities/detail/various-plugins-various-version-use-of-polyfillio
- https://codeql.github.com/codeql-query-help/javascript/js-functionality-from-untrusted-domain/
- https://cwe.mitre.org/data/definitions/830.html
- https://cheatsheetseries.owasp.org/cheatsheets/Third_Party_Javascript_Management_Cheat_Sheet.html
- https://www.w3.org/TR/SRI/
- https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/SRI
- https://semgrep.dev/blog/2024/protect-your-code-from-the-polyfill-supply-chain-attack/
- https://cert-agid.gov.it/news/scoperto-un-grave-attacco-alla-supply-chain-del-servizio-polyfill-io-piu-di-100-000-i-siti-coinvolti/
- https://isomer-user-content.by.gov.sg/36/8bee5efc-3166-44a1-89ae-0f0b095ecb17/03-July-2024.pdf
会员简报
深度档案背景
使用对应会员级别登录后,可解锁完整简报和来源说明。

