2026 年 9 月 21 日,全球下载量巨大的 TypeScript 类型验证库 Zod 代码仓库出现异动。一个伪装成性能优化的恶意提交被强行推入主分支,试图将版本号跳升至 4.6.6,并在安装流程中引入暗中调用以太坊智能合约的二阶段加载器。
然而社交网络上流传的感染恐慌并未成为现实。虽然攻击者拿到了代码推送权限,但受损代码从未进入 npm 官方注册表。现代软件供应链防御中部署的发布环境门禁与凭证隔离机制,在关键时刻切断了自动化恶意发布的链路,将一场可能席卷数百万项目的供应链危机拦在生产大门之外。
潜入主分支的木马:伪装成补丁的以太坊寻址器
根据记录,恶意提交 d821685 发生于 2026 年 9 月 21 日 11:56:52 UTC。攻击者打着修复缺陷与提升性能的旗号,对核心包与子模块进行了多处改动。其核心载荷是一个名为 meta.test.mjs 的隐蔽脚本,配合修改了相关的包配置文件。
这段代码采用了安全界称为 EtherHiding 的链上隐蔽信道技术。它没有在脚本中硬编码容易被静态扫描识别的恶意控制端域名,而是直接利用公共 RPC 节点,向以太坊合约发起调用。
具体而言,恶意载荷轮询 Cloudflare 与公共 RPC 节点,向合约地址发起调用,解析返回值以还原真实的控制服务器域名。在获取到后续载荷后,代码在临时目录解密并落盘,执行完内存载入后迅速将自身物理删除。这种把区块链当成不可篡改死信箱的手段,让传统的网络层封锁和静态特征库很难提前防御。
阻断与失误:双重关隘如何让投毒失效
尽管潜入手段精密,这次针对核心依赖的伏击最终却全面落空。维护者 Colin McDonnell 随后证实,自己的代码推送权限确实疑似外泄,攻击者借此直接越过了分支保护。但恶意代码在抵达开发者电脑前,遇到了两道不可逾越的屏障。
第一道屏障来自工程机制。黑客试图修改 GitHub Actions 的工作流配置,让提交事件自动触发发包。但 Zod 引入了 npm Trusted Publishing 机制与受保护环境。在自动化流水线中,发布任务受到环境审批策略的严格约束,哪怕流水线被触发,缺少合法维护者的人工批准,OIDC 临时令牌就不会下发。同批次攻击中,恶意包 @nubjs/nub@0.9.4 虽然一度到达发布流水线,同样在公开可用前被拦截。
挡住这场投毒的不是代码审查,而是流水线上不讲情面的发布审批门禁。
第二道屏障则源于攻击者自身的低级失误。代码审查显示,黑客将触发脚本运行的 preinstall 钩子错误地写在了配置文件的根层级,而不是标准的 scripts 对象内。按照包管理器的既有解析规则,即便这批软件包侥幸打包发布,用户在执行安装时也根本不会触发这段恶意载荷。
维护者随即通过 PR #6620 展开清洗,回滚了包括后续 4.6.7 与 4.6.8 在内的版本跳变提交。最终在 npm 注册表上发布的合法 zod@4.6.6,完全基于修复后的干净代码构建。
- 结论.只要下游工程使用官方 npm 镜像拉取依赖,生产环境便无需恐慌;真正的风险仅限于在事发数小时内直接锁定特定 Git 提交构建的极端场景。
现代流水线的防线启示
这场有惊无险的未遂事件,给近年来风声鹤唳的开源供应链安全提供了一个具象的解剖样本。攻击者的策略在快速演进:传统的投毒往往依赖高仿包名或窃取长期有效的发包密钥,而这起事件展现了更复杂的链条,黑客不仅尝试篡改持续集成配置,还动用了抗封锁的智能合约基础设施。
但它同样证明了防御体系重构的价值。以往开源项目极度依赖单一维度的开发者个人安全意识,私钥一旦外泄即全盘皆输。Zod 能够守住底线,本质上是因为现代发布流程将推送权限与发包权限做了强物理隔离。
- 提醒.开源项目切断直连发包权限虽能阻断投毒,但能被未授权提交直推主分支,说明敏感分支保护与推送凭证的多因素管控仍需加固。
对广大依赖开源库的开发团队而言,警报虽已解除,但排查姿态不能松懈。依赖管理工具锁死在官方 npm 语义化版本中的项目处于安全状态,但对于内部工作流中包含直接拉取主分支最新代码的工程,收紧对特定 Git 提交哈希的依赖策略,已是刻不容缓的防御功课。
