npm 默认封杀 postinstall 脚本:供应链安全的一次硬着陆
2025年3月,npm 官方宣布默认阻止 postinstall 脚本自动执行。这一决定直接回应了近年来愈演愈烈的供应链攻击——仅 2024 年就有超过 200 起通过 postinstall 脚本发起的恶意攻击事件。npm 的这一步,等于把过去默认信任的安装后执行机制,改成了默认怀疑。
postinstall 脚本:从便利到隐患
postinstall 脚本是 npm 包生命周期中的一环,在包安装完成后自动运行。它的初衷是方便开发者:比如自动编译原生模块、下载二进制依赖、生成配置文件。像 node-sass 需要从 GitHub 下载预编译的 Sass 库,sharp 需要拉取 libvips 二进制,这些工作都依赖 postinstall 脚本在安装时完成。
但便利的另一面是风险。postinstall 脚本在用户机器上以当前用户权限执行任意代码,这意味着一旦某个包被劫持,攻击者就能在安装瞬间植入恶意程序。最典型的案例是 2018 年的 event-stream 事件:一个流行的 npm 包被攻击者接管后,在 postinstall 脚本中注入了窃取比特币钱包的代码,下载量高达数百万次。2024 年的多起供应链攻击也延续了同样的套路——攻击者通过钓鱼或漏洞获得包维护者权限,然后在 postinstall 脚本中藏入挖矿木马或窃密程序。
postinstall 脚本之所以成为攻击者的首选载体,是因为它隐蔽且自动。开发者安装依赖时通常不会逐行审查每个包的脚本,而 npm 默认执行脚本的机制,让恶意代码几乎零门槛地进入开发环境。
npm 新政策:默认阻止,但可手动放行
npm 的新政策改变了这一默认行为。从 2025 年 3 月起,npm 在安装依赖时默认阻止 postinstall 脚本执行。这意味着,除非开发者明确允许,否则任何包的 postinstall 脚本都不会运行。
但这不是一刀切。npm 提供了手动放行的途径:开发者可以在 package.json 中配置 allowScripts 字段,列出允许执行脚本的包名;或者在命令行使用 –allow-scripts 标志,针对单次安装临时放行。这种设计试图在安全与兼容性之间找到平衡——默认阻止降低了风险,但保留了必要的灵活性。
npm 官方表示,这一变化是基于对生态中恶意脚本事件的长期观察。默认阻止并非彻底禁用,而是把决定权交还给开发者,让他们在知情的情况下选择信任哪些包。
安全与兼容性的博弈:新政策如何影响现有项目
新政策对现有项目的影响是直接的。许多广泛使用的包依赖 postinstall 脚本才能正常工作。node-sass 在安装时需要下载对应平台的二进制文件,sharp 需要编译原生模块,这些包在默认阻止下会安装失败或功能缺失。
对于依赖这些包的项目,开发者会看到安装警告或错误,提示某些脚本被跳过。如果项目依赖的包数量多,且其中不少使用了 postinstall 脚本,构建流程可能中断。npm 的默认阻止策略,实际上是把过去隐性的信任问题摆到了台面上——开发者必须明确决定哪些包可以执行脚本。
这种影响在 CI/CD 环境中尤为明显。自动化构建通常不会有人工干预,如果某个依赖的 postinstall 脚本被阻止,流水线可能直接失败。团队需要提前在配置中声明允许的脚本,否则就得面对构建中断的麻烦。
开发者应对指南:白名单配置与替代方案
面对新政策,开发者需要调整工作流。最直接的方式是配置白名单。在项目根目录的 .npmrc 文件中,可以添加类似 allow-scripts=true 的全局开关,但这会关闭所有保护,不推荐。更精细的做法是在 package.json 中设置 allowScripts 字段,列出需要执行脚本的包名。例如:
|
|
这样,只有列出的包能执行 postinstall 脚本,其他包保持默认阻止。
对于需要临时放行的情况,可以使用 npm install --allow-scripts 命令,但要注意这会让本次安装的所有包脚本都执行,适合在可信环境中使用。
替代方案方面,npm 官方推荐使用 npm rebuild 来手动触发已安装包的脚本执行。对于原生模块,可以考虑使用预编译的替代包,比如用 @img/sharp-wasm32 代替 sharp,避免依赖安装时下载二进制。此外,pnpm 提供了 pnpm approve-builds 命令,允许开发者逐个批准包的构建脚本,这种交互式方式比 npm 的静态配置更直观。
行业反应:其他包管理器的跟进与差异
npm 并非第一个对脚本执行设限的包管理器。pnpm 早在 2023 年就引入了 pnpm approve-builds 机制,默认阻止依赖的构建脚本,要求开发者明确批准。Yarn 则通过 yarn set resolution 和 yarn add --ignore-scripts 提供了类似的控制,但默认行为仍是执行脚本。
npm 的这次调整,实际上是向 pnpm 的严格模式靠拢。但两者有差异:pnpm 的批准机制是交互式的,安装时提示开发者选择;npm 则是静态配置,需要提前在 package.json 中声明。对于大型项目,npm 的静态配置更利于版本控制,但灵活性稍差。
行业内的这种趋势表明,包管理器正在从「默认信任」转向「默认怀疑」。npm 作为生态中用户量最大的包管理器,其政策变化可能带动其他生态跟进。例如,Python 的 pip 和 Rust 的 cargo 也在探索类似的脚本限制机制,但尚未有明确的时间表。
未来展望:供应链安全的新常态
npm 的默认阻止政策,标志着供应链安全进入新常态。对开源维护者来说,这意味着他们需要更谨慎地设计包的安装流程,避免依赖 postinstall 脚本完成关键功能,否则可能影响用户采用。对企业安全团队而言,这是一个利好——默认阻止降低了内部开发环境被恶意脚本入侵的风险,但同时也要求他们建立更完善的依赖审查流程。
对开发者个人,新政策增加了安装时的认知负担,但长远看是值得的。过去,一次 npm install 可能悄悄执行了数十个脚本,其中任何一个都可能是恶意的。现在,开发者至少知道哪些脚本在运行。
当然,攻击者不会坐以待毙。绕过手段可能包括:利用 preinstall 或 prepare 等其他生命周期脚本(npm 目前只默认阻止 postinstall,其他脚本仍会执行);或者通过依赖嵌套,让恶意脚本藏在间接依赖中,因为 allowScripts 配置可能只覆盖直接依赖。npm 后续可能需要扩大默认阻止的范围,或者提供更细粒度的控制。
目前还不清楚 npm 是否会进一步收紧,但可以确定的是,供应链安全不再只是安全团队的事,而是每个开发者在日常安装依赖时都要面对的现实。npm 的这一刀,切在了问题的核心上。
参考来源
- 原文作者:知识铺
- 原文链接:https://index.zshipu.com/ai001/post/20260822/npm-%E9%BB%98%E8%AE%A4%E5%B0%81%E6%9D%80-postinstall-%E8%84%9A%E6%9C%AC%E4%BE%9B%E5%BA%94%E9%93%BE%E5%AE%89%E5%85%A8%E7%9A%84%E4%B8%80%E6%AC%A1%E7%A1%AC%E7%9D%80%E9%99%86/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。
- 免责声明:本页面内容均来源于站内编辑发布,部分信息来源互联网,并不意味着本站赞同其观点或者证实其内容的真实性,如涉及版权等问题,请立即联系客服进行更改或删除,保证您的合法权益。转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。也可以邮件至 sblig@126.com