供应链投毒的共同模式:从 event-stream 到 xz

四次事件,一个共同点

过去几年最著名的几起开源供应链事件,技术细节各不相同,但模式惊人地一致:

  • event-stream(2018):原作者把维护权转给陌生人,新维护者在依赖里塞进针对特定钱包的窃密代码;
  • ua-parser-js(2021):维护者账号被劫持,发布带挖矿与窃密的版本;
  • colors(2022):维护者因不满商业使用,主动发布死循环版本,无数项目构建中断;
  • xz(2024):攻击者用两年时间扮演热心贡献者,逐步拿到维护权,在构建脚本里埋后门。

共同点是:信任建立在「人」身上,而不是「代码」上。 你锁定了版本号,但没锁定发布者;你审了源码,但没审构建脚本。

攻击者从哪进来

三条路径覆盖了绝大多数案例:

  1. 维护者账号:钓鱼、密码复用、npm token 泄露。拿到账号就能发新版本,签名和来源看起来都正常。
  2. 维护权转移:原维护者累了,把包交给「热心人」。event-stream 和 xz 都走了这条路。
  3. 安装期执行:postinstall 脚本、setup.py、构建脚本。代码在 CI 里跑起来,凭据就在环境变量里。

防御:把「信任」变成「可验证」

没有单一措施能根治,但可以把暴露窗口压到很小:

  • 锁文件 + 完整性哈希:package-lock.json、poetry.lock 里的 integrity 字段能发现内容被替换,但防不住「新版本本身就是恶意的」;
  • 私有代理与白名单:Nexus、Artifactory 这类制品库做缓存与准入,新包不会立刻进入生产;
  • 延迟升级:新版本发布后等几天再升,让社区先踩雷;
  • 审查安装脚本:CI 里禁用 postinstall(npm ci --ignore-scripts),确实需要的单独白名单;
  • 最小化 CI 凭据:发布 token 只给发布流水线,构建环境不挂生产密钥;
  • 出网限制:构建机默认不能访问外网,依赖只能从代理拉。

被忽略的一环:你自己发布的包

如果你维护着内部包或公开包,同样要防:开启 2FA、使用细粒度 token、开启 provenance 证明(npm 的 --provenance),让下游能验证「这个包确实由这条流水线构建」。

我的判断

供应链安全没法做到「零风险」,因为它本质上是把信任外包给了一群你素未谋面的人。能做的只有两件事:缩小信任面(少依赖、少权限),缩短发现时间(监控、比对、回滚能力)。

xz 那件事留下的教训最值得记住:攻击者不急着得手,他愿意等两年。防守方如果只盯着「今天有没有新 CVE」,永远慢一步。

延伸阅读

© 版权声明
THE END
喜欢就支持一下吧
点赞7 分享
评论 抢沙发
头像/SUENCA
欢迎您留下宝贵的见解!
提交
头像/SUENCA

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容