Back

npm 安全最佳实践

npm 安全最佳实践

npm 生态系统是世界上最大的包注册中心,这种规模使其成为高价值攻击目标。Shai-Hulud、event-stream 和 eslint-scope 等攻击事件都证明了同一个令人不安的事实:在你阅读源代码的第一行之前,安装一个包就可能在你的机器上执行任意代码。这些 npm 安全最佳实践可帮助你在每个阶段——安装、开发和发布——降低风险。

核心要点

  • 全局禁用安装后脚本,仅将真正需要它们的包加入白名单。
  • 设置发布时间冷却期,使全新(且可能是恶意的)版本永远不会自动进入你的构建。
  • 始终提交你的锁文件,并在 CI 流水线中使用 npm ci 以实现可重现、防篡改的安装。
  • 使用基于 WebAuthn 的双因素认证保护维护者账户,并通过 OIDC 可信发布来发布包,以消除长期有效的令牌。

为什么 npm 供应链安全现在至关重要

供应链攻击不会利用你的代码。它们利用的是你对他人代码的信任。当一个被入侵的包进入你的 node_modules 时,它可以窃取凭据、泄露环境变量或进一步传播自身。仅 Shai-Hulud 蠕虫一次事件就导致注册中心删除了 500 多个包。漏洞扫描本身无法捕获这类攻击——你需要纵深防御。

安全的依赖管理:加固你的安装

禁用安装后脚本

安装后脚本是 npm 供应链攻击中最常见的攻击向量。全局禁用它们:

npm config set ignore-scripts true
npm config set allow-git none   # npm CLI 11.10.0+

allow-git=none 设置很重要,因为基于 git 的依赖项可以附带自己的 .npmrc,静默地重新启用生命周期脚本,从而完全绕过 ignore-scripts

如果你使用 pnpm,版本 10+ 默认禁用安装后脚本。使用 pnpm-workspace.yaml 显式将确实需要它们的包加入白名单:

allowBuilds:
  esbuild: true
  core-js: false

启用 strictDepBuilds: true 可将任何未经审查的构建脚本转变为阻塞 CI 的失败,而不是静默警告。Bun 也默认阻止安装后脚本,可通过 package.json 中的 trustedDependencies 选择性启用。

当你确实需要生命周期脚本时,使用 @lavamoat/allow-scripts 创建可审计的白名单,而不是全局启用脚本。

添加发布时间冷却期

恶意包通常在数小时或数天内被发现并撤销发布。跳过全新版本可以给社区时间在问题到达你之前发现它们:

npm config set min-release-age 3

这告诉 npm 忽略任何发布时间少于三天的包版本。对于自动化依赖更新,像 RenovateDependabot 这样的工具支持等效的 minimumReleaseAge 设置。

提交你的锁文件并使用 npm ci

始终提交 package-lock.json。在 CI 环境中,将 npm install 替换为:

npm ci

npm ci 专门从锁文件安装,如果存在任何不匹配则失败,并且永远不会静默解析不同的版本。这是自动化流水线中可重现、安全构建的基础。

不要盲目升级

npm auditnpm outdated 是有用的信号,但将它们视为一个输入,而不是完整的解决方案。在升级任何依赖项之前,审查变更日志。避免跳过此步骤的批量升级——每次版本升级都是潜在的攻击面变化。

包维护者的安全 npm 工作流

启用双因素认证——并升级到 WebAuthn

账户接管是针对注册中心的供应链攻击的主要机制。在你的 npm 账户上启用双因素认证并设置写入模式保护:

npm profile enable-2fa auth-and-writes

GitHub 越来越多地鼓励使用密钥和基于 WebAuthn 的身份验证,它们比传统的基于 TOTP 的双因素认证更能抵御钓鱼攻击。硬件密钥或通行密钥比 TOTP 代码更难被钓鱼,因此尽早切换是值得的。

使用可信发布(OIDC)进行发布

长期有效的 npm 令牌是一种负担。通过 OIDC 的可信发布允许 GitHub Actions 或 GitLab CI 使用短期、工作流范围的凭据直接向 npm 进行身份验证——无需存储令牌。它还会自动生成来源证明,为消费者提供包构建位置和方式的加密证明。

这是生态系统的发展方向。GitHub 已表示计划逐步淘汰传统令牌,并使可信发布成为自动化的默认路径。

验证你安装的内容

不要假设官方注册中心是安全的。使用 Socketnpq 在安装前筛查包的可疑行为。检查下载次数、仓库活动和维护者历史——特别是对于 AI 编码助手建议的包,它们可能会幻想出包名称,然后被攻击者注册(一种称为 slopsquatting 的技术)。

结论

没有单一工具可以防止所有 npm 供应链攻击。最重要的实践是分层的:禁用生命周期脚本、强制使用锁文件、对新版本添加冷却期、在 CI 中使用 npm ci,并将发布迁移到带有双因素认证的 OIDC。每一层都独立降低风险。结合在一起,它们使你的工作流更难被攻破。

常见问题

是的,某些包如 esbuild 或 sharp 需要安装后脚本来下载特定平台的二进制文件。与其全局重新启用脚本,不如使用白名单方法,通过 pnpm 的 allowBuilds 配置或 LavaMoat 的 allow-scripts 仅向确实需要构建步骤的特定可信包授予权限。

不够。npm audit 检查已发布公告中的已知漏洞,但它无法检测新型供应链攻击,如错别字抢注、账户接管或注入到原本合法包中的恶意代码。将 audit 视为更广泛的分层防御策略中的一个有用信号,该策略还包括锁文件强制执行、脚本限制和预安装筛查工具。

npm install 读取 package.json,解析依赖范围,并可能修改锁文件。npm ci 仅从 package-lock.json 读取,安装精确版本,如果锁文件缺失或与 package.json 不一致则立即失败。在持续集成流水线中始终使用 npm ci 以保证可重现和防篡改的构建。

传统的 npm 令牌是存储在 CI 环境中的长期有效密钥,使其成为有吸引力的盗窃目标。OIDC 可信发布生成与特定仓库和操作运行绑定的短期、工作流范围的凭据。没有密钥存储在任何地方,每次发布操作都以加密方式链接到其源,自动生成可验证的来源证明。

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay