
Node.js 测试 CI 安全事件全披露TOCTOU 竞态漏洞分析、攻击链复盘与基础设施加固实践【免费下载链接】nodejs.orgThe Node.js® Website项目地址: https://gitcode.com/GitHub_Trending/no/nodejs.org2025 年 3 月 21 日Node.js 项目通过 HackerOne 漏洞赏金计划收到一份安全报告披露其多个测试 CI 主机被成功入侵随后项目官方于 4 月 23 日发布完整披露原始文档位于 apps/site/pages/en/blog/vulnerability/march-2025-ci-incident.md。本篇文章以该披露为主体系统拆解攻击者如何借力 PR 评审流程触发 Jenkins 流水线、利用检查时间与使用时间不一致Time-of-Check-Time-of-UseTOCTOU竞态完成代码执行并完整梳理 Node.js 团队的补救措施、修复模型与逐日时间线。读完本文你将理解开源项目 CI 基础设施中可变 Git 引用 延迟检出的经典风险模型掌握以提交 SHA 校验为核心的可信执行加固思路并了解 300 志愿者如何在大规模 CI 集群上平衡安全与开发者体验。事件摘要一次针对 CI 基础设施的定向入侵2025 年 3 月 21 日Node.js 项目收到一份经 HackerOne 提交的安全报告披露时该报告链接处于受限状态内容详述了攻击者对多个 Node.js 测试 CI 主机的成功入侵。报告描述了完整的利用链项目方在复核后确认报告真实有效。需要首先明确两个关键边界事实官方披露原文明确声明该事件不影响 Node.js 运行时本身对所有 Node.js 用户不存在运行时风险用户无需采取任何操作受影响的是开发基础设施测试 CI 主机事件发生后项目方立即限制相关访问并着手整改。攻击链还原五步利用 request-ci 标签达成代码执行根据 HackerOne 报告攻击者按如下五个步骤完成了攻击提交一个有效的 pull request到 nodejs/node 仓库等待维护者添加request-ci标签——该标签会被添加到每一个包含非文档变更的 PR 上即 CI 触发授权信号批准之后使用过时的 Git 提交时间戳更新该 PR当 Jenkins 流水线触发时它从 fork 出的 PR 中拉取并执行代码在 Node.js Jenkins agent 上取得代码执行权限。下图展示了正常场景下在 GitHub Pull Request 上启动 Jenkins CI的完整工作流正常流程中维护者Maintainer为 PR 添加request-ci标签 → 系统执行更安全的验证Certify Safer validation即确认该 PR 是否由维护者请求、且自添加标签以来 PR 内容未发生变化→ 验证通过后 Jenkins 执行Fetch /PR_ID/HEAD→ 代码分发到node-test-1、node-test-2……node-test-N组成的 Jenkins Test CI 集群执行测试。而攻击的关键恰恰发生在验证与取出代码之间的时间窗口内如下图的攻击示例所示攻击者在 PR 通过验证just validated之后、Jenkins 实际检出代码之前发送一个提交改变该 PR 的 HEAD——此时 Jenkins 取到的已不再是维护者验证过的代码攻击代码随流水线执行落到 agent 上形成典型的 TOCTOU 竞态。关键研判request-ci 并非攻击的必要条件Node.js 团队在复核中进一步确认了一个更严峻的事实request-ci标签步骤简化了攻击但并非攻击的必要条件。同一类攻击同样适用于commit-queue标签——这意味着攻击者理论上可以通过该标签未经授权地向主干合入一段代码变更威胁等级从CI 机器被控上升为仓库内容被篡改。根因剖析触发与检出之间的可变引用窗口官方披露明确指出核心问题出在CI 构建发起与 Jenkins 作业检出代码之间存在 TOCTOU 漏洞此前CI 作业使用Git 引用refs/pull/pr_id/head来定位代码这种可变引用在 CI 触发之后仍然可以被攻击者更改PR 作者可以持续推送新提交引用指向随之漂移因此发起 CI 的协作维护者并没有做错任何事——CI 被触发时PR 看起来确实是安全的问题出在引用可以变而非已变坏。补救措施从止血、重建到机制升级的六项动作确认漏洞后Node.js 安全团队采取了一系列措施以缓解风险并加固基础设施立即限制新 Jenkins CI 运行的发起权限在团队验证报告期间暂停一切新的 CI 启动入口防止进一步失陷识别、下线并重建全部受感染主机共 24 台机器被迅速定位、从 Jenkins 移除并重建以消除初始入侵残留的任何潜在风险在 Jenkins 作业中实施提交 SHA 校验作业现在只执行经过验证、可信的代码request-ci与commit-queue标签改为依赖验证过的提交 SHA不再依赖日期比较此前的PR 自加标签以来未变化是基于时间戳推断的这正是竞态被利用的缝隙对 140 个 Jenkins 作业执行全面审计优先审计高频作业以发现并修复漏洞排查 GitHub 工作流识别出存在相似攻击面的 GitHub workflows 后先临时禁用随即打补丁并以增强后的安全措施重新启用。修复后的新验证模型强制审批 显式 SHA官方披露给出了修复后的核心行为变化这是整套加固中最具参考价值的设计决策现在每个 PR 都必须先经过批准approval才能运行 Jenkins CI——或者由协作者显式指定具体的 SHA。也就是说CI 执行对象从一个可变引用变为一个不可变的提交标识要么该 PR 已被授权者批准信任链前置要么调用方直接给出期望的提交 SHAJenkins 在机器上检出代码后校验实际 SHA 与期望值一致才继续执行。从源码结构看这等价于把信任判断从 CI 触发后的异步时点前移并固化为触发时点的显式参数从而从根本上封堵 TOCTOU 窗口。完整时间线3 月 21 日—4 月 3 日的处置实录官方披露附带了逐日处置记录完整还原了这次事件从收到报告到恢复常态的过程日期处置内容2025-03-21周五收到 HackerOne 报告初步分流确认是真实问题限制启动新 Jenkins CI 的能力防止进一步机器失陷2025-03-24周一全部 24 台失陷机器被定位并从 Jenkins 移除待完整重建开始评估 Jenkins 实例中定义的 140 个作业的脆弱性着手改造最高频使用的易感作业——要求其接收期望的提交 SHA仅当机器上检出的代码 SHA 匹配时才继续2025-03-25周二部分受影响主机完成重建更新后的作业在 macOS 上失败经调查后再次更新2025-03-26周三更多作业更新完成、受影响主机重建部分 GitHub 工作流被识别为存在相似攻击面并予以禁用2025-03-27周四再次调整更新后作业的校验逻辑以允许非 PR 分支上的日常测试决定禁用所有尚未评估或尚未应用修复的剩余作业更多机器完成重建2025-03-28周五GitHub 工作流完成修补commit-queue重新启用2025-04-01周二恢复在 Jenkins 及通过request-ci启动作业的能力部分使用频率较低的作业仍保持禁用2025-04-02周三更多机器完成重建2025-04-03周四Benchmarking 与 libuv CI 作业完成更新这条时间线清晰地展示了处置策略的三个阶段紧急止血21 日—24 日→ 分批修复与重建24 日—27 日→ 验证后逐步恢复28 日—4 月 3 日。值得注意的是宁可全部禁用、不可带病运行的决策——27 日团队选择禁用所有尚未完成评估或修复的作业宁可牺牲 CI 可用性也要保证不再发生二次失陷。安全与开发者体验的平衡大规模开源 CI 的现实设计官方披露给出了项目 CI 体系的量级背景这有助于理解为什么此类风险难以提前杜绝Node.js 项目由300 志愿者共同维护其流程服务于约 100 台 Jenkins runner横跨多个操作系统与 CPU 架构现有的 CI 系统设计本身预设了可能被攻破的假设承认需要在安全性与开发者便利性之间做出权衡。也就是说在任何人可通过 PR 贡献代码 维护者快速发起 CI 验证的高吞吐协作模型下可变 Git 引用被用作 CI 输入是一种便利性选择本次事件正是这种便利性的代价而 SHA 校验模型则是团队在权衡后给出的收敛答案。志愿者组织的现实约束与安全研究边界作为志愿者驱动型组织Node.js 依赖成员将时间投入到 CI 加固、安全报告处理、版本发布等不显眼的工作上。披露文档特别指出即便是针对生产系统的善意安全研究也可能严重干扰项目运作。因此项目方在欢迎包括渗透测试在内的各类贡献的同时明确要求研究者在针对生产系统开展任何尝试前先行沟通并通过 HackerOne 漏洞赏金计划或直接联系 Node.js 技术指导委员会TSCtsciojs.org保留可审计的操作记录。这一约定对任何参与开源安全研究的人都是重要提醒知情、受控、可审计的研究与直接打线上系统之间存在本质区别后者即便动机善意也可能造成服务中断。对 Node.js 用户的结论与后续信息渠道结论本次事件不涉及 Node.js 运行时没有任何 Node.js 用户受影响也不需要采取任何行动受影响的开发基础设施原计划在 2025 年 4 月 15 日或更早恢复社区可用完整披露随后发布。报告 Node.js 漏洞可遵循 Node.js 官方安全策略中的流程就本网站仓库nodejs.org而言安全问题的正确上报方式是使用 GitHub Security Advisory 的私有上报流程而不是公开 issue——具体响应承诺7 个工作日内确认与披露渠道见本仓库根目录的 SECURITY.md。持续跟踪安全动态可订阅低流量、仅发布公告的 nodejs-sec 邮件列表以第一时间获知 Node.js 及其维护组织内各项目的安全漏洞与安全相关版本发布。本次事件带来的工程启示回顾整个披露可以提炼出几条对任何 CI/CD 基础设施都有普适价值的结论可变引用不应成为执行信任锚点refs/pull/pr_id/head这类随时可漂移的引用天然存在验证与使用分离的竞态窗口以不可变的提交 SHA作为执行凭证是关闭 TOCTOU 窗口的直接手段。信任判断的时点必须前移把谁有权触发、针对哪个版本触发固化为触发时刻的显式参数审批 SHA优于事后依赖时间戳的推断式校验。攻破面评估要覆盖整条链路本案中漏洞不仅影响request-ci还波及commit-queue与 GitHub workflows——凡是根据外部事件拉取并执行代码的环节都应纳入同一套校验模型。应急响应要敢于整体降级在无法逐一确认安全性的情况下暂时禁用全部未评估作业是用 CI 可用性换取基础设施可信度的务实决策。Node.js 项目的这份披露文档本身也是开源安全事件公开透明的范本完整的攻击链、明确的根因、可复盘的补救措施与逐日时间线全部向社区开放。对于任何自建 CI 或参与大型开源项目维护的工程师而言这份记录都值得精读与对照自查。延伸阅读本文分析所依据的官方披露原文见 march-2025-ci-incident.md两张架构图源文件见 example_test_infra.svg 与 example_attack_test_infra.svgNode.js 项目常规漏洞披露的公告体例可对照 may-2025-security-releases.md本仓库的安全上报流程见 SECURITY.md。【免费下载链接】nodejs.orgThe Node.js® Website项目地址: https://gitcode.com/GitHub_Trending/no/nodejs.org创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考