恶意包平均活不过 3 天,但你装它的速度是 0 秒——npm 终于把「新版本先冷静 24 小时」做成了默认能力
导语:供应链投毒的窗口期其实很短——多数恶意版本在上架后几小时到几天内就会被下架。问题在于,npm install 过去是「发布即可装」,这个检测窗口对你完全不存在。npm CLI 11.10.0 起补上了 minimumReleaseAge,pnpm / Yarn / Bun 早就有的「发布冷却期」,现在三大包管理器全线对齐了。
先说结论:这条能力不是让你更安全一点,而是把「发布到安装」之间那段本该存在的缓冲带还给了你。真正要改的,是你 CI 里那条不带任何门禁的 npm install。
问题出在时间轴上
一次投毒攻击能成功,靠的是三个时间点的错位:
- T0 发布:攻击者上传恶意版本(劫持维护者账号、typosquatting、或者污染传递依赖)。
- T1 被发现:社区或扫描工具识别并举报,npm 下架。现实中这个区间常常是几小时到几天。
- T2 你安装:
npm install解析到最新版本,装进来。
过去 T2 是「几乎等于 T0」——你随一次常规依赖升级就把恶意版本拉进了构建。minimumReleaseAge 做的事很朴素:在解析阶段直接忽略掉发布时间不足 N 分钟的版本,把 T2 强行推到 T1 之后。
三条落地路径
1. npm:minimumReleaseAge
npm CLI 11.10.0 引入 minimumReleaseAge(单位:分钟)。配置在 .npmrc:
# 24 小时冷却,1470 = 24*60 + 30 分钟的余量
minimum-release-age=1440
或等价的环境变量 / 命令行方式:
npm install --minimum-release-age=1440
语义要点:它不是「跳过更新」,而是「对每个候选版本做年龄过滤」。已经锁在 lockfile 里的版本不受影响;只有当解析器要升级到一个新版本时,这个新版本的发布时间才需要超过阈值。
2. pnpm:默认就带 24 小时隔离
pnpm 在这条路上走得更早,v11 起把「24 小时 quarantine + 拦截 exotic 子依赖」做成了默认行为,而不是可选项。想在 workspace 里手动加固,写进 pnpm-workspace.yaml:
packages:
- "."
- "packages/*"
# 7 天冷却:10080 分钟,给社区足够时间发现并举报
minimumReleaseAge: 10080
几组常用值:
1440— 24 小时,最激进的可用档10080— 7 天,安全与及时打补丁之间比较稳的平衡点- 不写 — 等于 0,任何新版本都能立刻解析(也就是过去的默认)
3. 顺手把「非 registry 安装源」也收口
npm 11.10.0 引入 --allow-git,11.15.0 又补齐了另外三个,覆盖所有非 registry 的安装来源:
npm install
--allow-git=none
--allow-file=none
--allow-remote=none
--allow-directory=none
这四个 flag 每个接受 all(当前默认)或 none:
--allow-git:git 源,含github:、gitlab:、git+URL 和owner/repo简写。历史缺口在于:git 依赖(直接或传递)可以夹带.npmrc覆盖 git 可执行文件路径,从而在--ignore-scripts已经开启的情况下依然执行任意代码。--allow-file:本地文件路径 / 本地 tarball。--allow-remote:远程 URL,含 https tarball。--allow-directory:本地目录。
官方明确说了:--allow-git 会在 npm CLI v12 把默认值从 all 改成 none。也就是说现在开启 =none,只是提前适应未来的默认。可以在 .npmrc 或 package.json 的 config 里持久化。
CI 里真正该做的三件事
只加一个配置解决不了问题,把门禁串起来才有意义:
- 冷却期设为默认:CI 用的
.npmrc里固定minimum-release-age,让「秒装最新版」从流程上不可能发生。 - 收口全部安装源:四个
--allow-*=none一起上,需要哪个再单独开哪个——显式白名单,而不是默认放行。 - 发布侧配 OIDC 而非长期 token:npm 11.10.0 的
npm trust支持批量配置可信发布,配合 11.15.0 的 staged publishing(发布先进 stage 队列,维护者过 2FA 才放行)。安装侧冷却 + 发布侧 proof of presence,两头都堵。
判断标准
如果你的项目里还留着一条 npm install(或 pnpm install)没有任何 release-age 约束,那就等于把上面那条「T1 检测窗口」直接删掉了。判断方法很简单:翻一眼 CI 配置,看有没有一行 minimum-release-age——没有就是没门禁。
下一步的最小动作:今天就在 .npmrc 里加一行 minimum-release-age=1440,跑一次 npm install 确认解析没被卡住,然后按团队对「最新版依赖」的容忍度往上调。补丁及时性和供应链安全之间没有标准答案,但「默认零冷却」绝不该是那个答案。
评论区
登录后可评论。