如何利用 pnpm 的安全控制功能防御 npm 供应链攻击

原文地址: How We're Protecting Our Newsroom from npm Supply Chain Attacks

本文并非原文的翻译,是经过我自己理解后的输出,聚焦我感兴趣或有价值的内容。对具体内容感兴趣的朋友可以去原文查看完整内容。

背景

2025 年 11 月,npm 供应链出现了蠕虫病毒攻击。很多基础且下载量较大的包都被感染,造成了非常严重的影响。而西雅图时报因为使用 pnpm 提供的安全控制功能,成功躲过了这次攻击。靠的不是什么高深的手段,而是把那段时间的依赖安装和升级"挡在门外"。

当时我们公司也因为这个事情紧急成立了安全小组,对项目中的 npm 依赖进行排查。尽管我们也没有受到影响,但让我们开始重视起安全这件事。对于大公司或者稳定的系统来说,安全与合规可能更重要。

客户端控制与 3 层纵深防线

npm 作为 JavaScript 生态最大的包管理工具,尽管其提供了一定程度的安全措施,但仍然属于发布端/平台的范围。作为使用者,也应该在客户端重视起来。

此前的供应链攻击,就是相关包作者被"钓鱼邮件"窃取了 npm 的账号,然后发布了带有恶意代码的依赖包,并在安装时利用 preinstall 或者 postinstall 钩子进行攻击。如果作为使用者,完全相信 npm 而没有一点防范意识,就很容易中招。

pnpm 则提供了一些安全控制功能,利用这些功能就可以建立起 3 层纵深防线。

1. 脚本生命周期管理(Lifecycle Script Management)

这次攻击最关键的一点,就是攻击者利用了 preinstallpostinstall 在安装依赖时植入恶意脚本。

pnpm 提供了 strictDepBuilds 选项,当设置为 true 时,会默认阻止依赖包执行构建脚本(除非显式允许)。你也可以通过配置,仅允许可信来源的包执行必要的脚本。

yaml 复制代码
strictDepBuilds: true

onlyBuiltDependencies:
  - package-with-necessary-build-scripts

ignoredBuiltDependencies:
  - package-with-unnecessary-build-scripts

2. 延迟升级(Release Cooldown)

通常情况下,npm 上的包更新速度会比较快。如果没有使用 lock 文件去锁定依赖版本,那每次安装就很可能在不知道的情况下升级,就有可能引入风险。

pnpm 提供了 minimumReleaseAge 选项,相当于给了一个冷静期,比如设置为在最新版本发布后一周再允许升级。这样可以对新版本的稳定性进行一段时间的观察。对于成熟的系统来说,稳定性往往比追新更重要。同样地,也可以设置白名单,用来应对一些紧急的 CVE 修复补丁。

yaml 复制代码
minimumReleaseAge: <duration-in-minutes>

minimumReleaseAgeExclude:
  - package-with-critical-hotfix@1.2.3

3. 信任策略(Trust Policy)

这个特性也是我从这篇文章中第一次知道的。它的作用是当软件包的信任级别低于之前版本时,就会阻止安装。简单来说,如果一个包之前一直是通过 CI/CD 流程发布,这一次却突然变成了本地提交发布,那么系统就会怀疑作者凭据可能被窃取,从而阻止安装。

其原理是 npm 会有三个信任级别(从高到低):

  1. 受信发布者:通过 GitHub Actions 发布,带有 OIDC 和 npm provenance(出处证明)。
  2. 发布来源:来自 CI/CD 的签名证明。
  3. 非受信:使用用户名/密码或者 token 进行发布。
yaml 复制代码
trustPolicy: no-downgrade

trustPolicyExclude:
  - package-that-migrated-cicd@1.2.3

总结

安全从来不是单点防御,而是一套纵深体系。

pnpm 的这三项能力------脚本生命周期管理、延迟升级、信任策略------本质上是在三个关键点"把闸门关上":安装时不轻易执行脚本、升级时给新版本留观察期、发布来源不允许突然降级。

这些策略可能会带来一点"麻烦"(比如需要维护白名单),但相比供应链攻击的破坏性,这点成本往往是值得的。而且这一套"纵深防线"的理念非常值得实践,执行起来也并不复杂:先从团队的核心项目开始试运行,逐步固化成默认配置,让安全不再依赖运气。

欢迎关注公众号:此方的手帐

个人博客: konata9.cc/

每周分享周刊/小知识,和你一起进步

相关推荐
To_OC35 分钟前
LC 22 括号生成:刷完这道题,我终于搞懂回溯剪枝了
javascript·算法·leetcode
小二·37 分钟前
Next.js 15 企业级实战:从项目搭建到CI/CD全链路(App Router + Server Components + Turbopack)
javascript·ci/cd·turbopack
像我这样帅的人丶你还40 分钟前
🚀大文件上传的那些事
前端·javascript·架构
To_OC1 小时前
LC 39 组合总和:回溯入门必刷题,我踩过的两个坑都在这了
javascript·算法·leetcode
橘子星1 小时前
从零理解流式输出 —— 一个 Vue + DeepSeek 的前端实战
前端·javascript·人工智能
এ慕ོ冬℘゜2 小时前
深入 JavaScript BOM:掌握 window 对象的窗口控制魔法
开发语言·javascript·ecmascript
Highcharts3 小时前
Highcharts 矩形树图 Treemap 示列|2025年多层级出口商品结构可视化
javascript·数据可视化
大家的林语冰4 小时前
✌️ Deno 2.9 来了,ESM 直接导入 CSS,甚至能开发桌面应用?!
前端·javascript·node.js
不简说4 小时前
JS 代码技巧 vol.1 — 10 个让代码少写 30% 的小套路
前端·javascript·面试
@@@@@@@@545 小时前
JavaScript 的缺点与 TypeScript 的弥补
开发语言·javascript·typescript