写过前端的人都踩过这个坑——每次以为 npm install 只是装依赖,今天 npm v12 把 16 年来默认执行别人代码的那扇后门彻底锁死了

每次 npm install 跑完,你的机器上其实已经执行了一堆你不认识的代码——只是你不知道。

这个状态持续了 16 年。

npm 设计之初就允许 package.json 里的 preinstall、install、postinstall 钩子在安装完成后自动运行。这些钩子可以编译原生模块、拉取平台相关二进制、跑构建步骤,对很多正经包来说是必要的能力。

问题在于:这些钩子不只跑顶层依赖的。依赖树里四五层深的那个你从来没手动选过的包,只要它声明了 install 脚本,就会和顶层包一样自动执行。没有提示,没有确认,执行权限和运行 npm install 的用户权限完全相同。

直到 2026 年 7 月 8 日,这个默认从来没变过。

2026 年 3 月,与朝鲜黑客组织 Sapphire Sleet 相关的攻击者劫持了 Axios 的维护者账号——这个 HTTP 客户端库每周下载量约一亿次——通过 postinstall 钩子植入了恶意代码。影响范围远超 Axios 本身,甚至波及了 OpenAI 的 macOS 代码签名基础设施。

2026 年 6 月,同一组织在约 88 分钟内自动入侵了 Mastra AI 框架 npm 作用域下的 144 个包,手段完全相同:postinstall 钩子。

这两个案例利用的都是「npm install 默认执行所有声明了的 install 脚本」这个特性。

7 月 8 日,GitHub 正式发布 npm v12,改变了三个安装行为的默认设置:

allowScripts 默认关闭

preinstall、install、postinstall 脚本不再自动执行,node-gyp 的隐式编译也被一同禁止。需要显式审批:

# 扫描项目里哪些依赖试图跑脚本
npm approve-scripts --allow-scripts-pending

# 审批信任的脚本,结果写入 package.json
npm approve-scripts <package-name>

# 对所有依赖全部放行(仅迁移过渡期使用)
npm approve-scripts --allow-all-scripts

批准后的配置存在 package.json 里,可提交 Git,跨环境可复现。

–allow-git 默认 none

直接和传递依赖里的 Git 来源不再自动解析,必须 npm install --allow-git 显式放行。这一项堵住的是一个已知攻击路径:恶意 Git 依赖里的 .npmrc 可以覆盖系统 Git 可执行文件路径,在 --ignore-scripts 已开启的情况下仍然实现代码执行。

–allow-remote 默认 none

来自远程 URL(如 HTTPS tarball)的依赖不再自动解析,必须 npm install --allow-remote 显式放行。本地路径 --allow-file 和 --allow-directory 行为不变。

行为 npm v12 之前 npm v12 之后
install 脚本 每个依赖自动执行 必须 npm approve-scripts 审批
node-gyp 隐式编译 自动执行 同上,统一拦掉
Git 依赖解析 自动 必须 –allow-git
远程 tarball 自动 必须 –allow-remote

npm v12 还启动了 2FA-bypass 精细访问 Token 的废止计划:

  • 2026 年 8 月起,配置了绕过 2FA 的 Token 不能执行账户管理操作(创建 Token、改密码/邮箱、改包权限等)
  • 2027 年 1 月起,这类 Token 不能直接发布包,只能暂存,发布须人工 2FA 审批

自动化发布的正确方式是 Trusted Publishing(OIDC),GitHub Actions 可在发布时申请短期 Token,不再需要持有长期发布 Token。

第一步:升级到 npm 11.16.0+(显示警告但不阻断),跑一次完整安装,检查哪些包有脚本被拦截:

npm install 2>&1 | grep "npm warn"

第二步:对确认可信的包执行 npm approve-scripts,把结果 commit 到仓库。

第三步:把自动化发布 Pipeline 里的长期 NPM_TOKEN 切换到 OIDC Trusted Publishing(GitHub Actions 有官方 Action 支持)。

这个改动在安全上收益极高,迁移成本对大多数项目来说可控。真正受影响的是那些「从来没人审过 install 脚本在干什么」的老项目——这类项目正好是最需要跑的。


下一步: 在本地跑一次 npm install,看有没有警告弹出来;有的话先不要直接 --allow-all-scripts,逐个包审批并 commit package.json 的改动。

评论区

0 条评论

登录后可评论。