2025 年 Shai-Hulud 等多波供应链攻击事件暴露出 JavaScript 生态的脆弱性:攻击者通过窃取长期有效的经典令牌(classic token),在 CI/CD 环境中自动化植入恶意代码,短时间内影响数千个下游包。作为回应,GitHub 与 npm 于 2026 年 5 月 22 日正式推出两项关键安全功能 ——分阶段发布(Staged Publishing)与安装时控制(Install-time Controls),试图在发布流程与依赖安装两个关键环节建立可审计、可拦截的安全防线。
分阶段发布:在 "发布" 与 "公开" 之间插入人工审批
传统 npm 发布流程中,npm publish执行后包版本立即对全球开发者可见。这种设计虽然优化了发布效率,但也意味着一旦 CI 环境凭证泄露,恶意版本可在数秒内扩散至整个依赖树。
分阶段发布改变了这一机制。当维护者执行npm stage publish(或 CI 自动执行)时,预构建的 tarball 被上传至暂存队列,而非直接公开。此时,包版本在 npmjs.com 和 CLI 中均处于 "待审批" 状态,必须由具备 2FA 验证的人类维护者显式批准后,才会正式对外发布。
这一设计的关键价值在于引入 "人为检查点"。即使攻击者成功入侵 CI 环境并触发发布指令,缺乏 2FA 验证的恶意版本仍被困在暂存队列中,无法进入公共 registry。对于使用 OIDC 可信发布(Trusted Publishing)的团队,还可将发布配置限制为 "仅暂存" 模式,确保 CI 工作流只能推送至队列,彻底阻断自动化直接发布路径。
实施分阶段发布需要 npm CLI 11.15.0 或更高版本。CI/CD 工作流需将npm publish替换为npm stage publish,同时维护者需建立对暂存队列的监控机制 —— 可通过 npmjs.com 网站或 CLI 命令npm stage list查看待审批版本。
安装时控制:限制非 registry 依赖源的四大开关
供应链攻击的另一常见路径是利用非 registry 安装源执行恶意代码。攻击者可能在 Git 仓库、远程 URL 或本地文件中植入 payload,通过npm install的生命周期脚本在开发者机器上执行。
npm 11.15.0 引入了三个新的安装时控制 flag,与 2026 年 2 月发布的--allow-git共同构成完整的非 registry 源管控体系:
| Flag | 控制范围 | 可选值 |
|---|---|---|
--allow-file |
本地文件路径与本地 tarball | all(默认)/ none |
--allow-remote |
远程 URL(如 https tarball) | all(默认)/ none |
--allow-directory |
本地目录 | all(默认)/ none |
--allow-git |
Git 源(github:、gitlab:、git + 等) | all(默认)/ none |
这些 flag 可在命令行直接使用,也可配置于.npmrc或package.json中,便于团队统一策略。值得注意的是,npm CLI v12 将改变--allow-git的默认值为none,意味着未来版本将默认禁止从 Git 源安装依赖。建议团队提前评估当前依赖树中是否存在 Git 源依赖,并决定是清理迁移还是显式开启允许策略。
工程实施:从配置到策略的完整路径
将这两项安全功能落地到生产环境,需要技术升级与流程调整并重。
CLI 与 CI/CD 升级清单:
- 全局或项目级升级 npm CLI 至 11.15.0+
- 将
npm publish替换为npm stage publish - 配置 OIDC 可信发布并启用 "仅暂存" 模式(可选但推荐)
- 在 CI 环境中设置
--allow-*flag 为none或根据需求显式允许
团队策略建议:
- 审批责任分配:明确哪些维护者拥有暂存队列审批权限,建议核心包至少配置两名审批人以实现职责分离
- 审批时间窗口:设定合理的审批 SLA,平衡安全与发布效率,避免紧急修复因审批延迟而受阻
- 依赖源审计:运行
npm ls结合--allow-*flag 测试,识别当前依赖树中的非 registry 源,评估迁移成本 - 监控与告警:对暂存队列设置通知机制,确保待审批版本不会被遗漏
局限性与补充措施
尽管分阶段发布与安装时控制提供了重要的安全层,但维护者仍需意识到当前机制的边界。
首先,OIDC 可信发布目前不支持首次发布新包,这意味着新包的首次发布仍需依赖令牌认证。其次,对于管理数十甚至数百个包的大型维护者团队,逐个配置可信发布与暂存策略仍是一项繁重的手动工作 —— 虽然 GitHub 在 2026 年 2 月推出了批量可信发布配置功能,但 API 覆盖度与自动化程度仍有提升空间。
此外,这些措施主要聚焦于 "凭证泄露后的拦截",而非 "凭证泄露的预防"。ESLint 创始人 Nicholas C. Zakas 指出,npm 在异常行为检测方面仍有改进空间,例如基于地理位置的发布异常告警、生命周期脚本变更的版本限制等。建议高影响项目将分阶段发布与 SAST 工具、依赖扫描、运行时监控结合使用,构建纵深防御体系。
结语
供应链安全没有银弹。分阶段发布通过 "人为审批点" 降低了自动化攻击的扩散速度,安装时控制则缩小了恶意代码的执行面。两者结合 OIDC 可信发布,为 JavaScript 生态提供了可落地的防护框架。对于企业团队而言,尽早升级 CLI、调整 CI/CD 流程、制定审批策略,是将这些安全能力转化为实际防护效果的关键步骤。
资料来源
- GitHub Changelog: "Staged publishing and new install-time controls for npm" (2026-05-22)
- Socket.dev: "npm to Implement Staged Publishing After Turbulent Shift Off Classic Tokens" (2026-01-07)
内容声明:本文无广告投放、无付费植入。
如有事实性问题,欢迎发送勘误至 i@hotdrydog.com。