Skip to content

NPM Security best practices / NPM 安全最佳实践

The following cheatsheet covers several npm security best practices and productivity tips, useful for JavaScript and Node.js developers. This list was originally based on the 10 npm security best practices from the Snyk blog.

以下速查表涵盖了多个 npm 安全最佳实践和生产力技巧,对 JavaScript 和 Node.js 开发者非常有用。此列表最初基于 Snyk 博客中的 10 个 npm 安全最佳实践。

1) Avoid publishing secrets to the npm registry / 1) 避免将秘密发布到 npm 注册表

Whether you’re making use of API keys, passwords or other secrets, they can very easily end up leaking into source control or even a published package on the public npm registry. You may have secrets in your working directory in designated files such as a .env which should be added to a .gitignore to avoid committing it to a SCM, but what happens when you publish an npm package from the project’s directory?

无论您是否使用 API 密钥、密码或其他秘密,它们很容易泄露到源代码控制或甚至发布到公共 npm 注册表上的一个包中。您的工作目录中可能包含秘密,例如在 .env 文件中,这些文件应该添加到 .gitignore 中以避免将其提交到 SCM,但当你从项目的目录中发布 npm 包时会发生什么?

The npm CLI packs up a project into a tar archive (tarball) in order to push it to the registry. The following criteria determine which files and directories are added to the tarball:

npm CLI 将项目打包成一个 tar 归档(tarball),以便将其推送到注册表。以下标准决定了哪些文件和目录被添加到 tarball 中:

  • If there is either a .gitignore or a .npmignore file, the contents of the file are used as an ignore pattern when preparing the package for publication.
    中文:如果存在 .gitignore 或 .npmignore 文件,则将文件内容用作忽略模式,在准备发布包时使用。
  • If both ignore files exist, everything not located in .npmignore is published to the registry. This condition is a common source of confusion and is a problem that can lead to leaking secrets.
    中文:如果两个忽略文件都存在,则不在 .npmignore 中的所有内容都将发布到注册表。这种情况是常见的混淆来源,也可能导致秘密泄露的问题。

Developers may end up updating the .gitignore file, but forget to update .npmignore as well, which can lead to a potentially sensitive file not being pushed to source control, but still being included in the npm package.

开发者可能最终会更新 .gitignore 文件,但忘记同时更新 .npmignore,这可能导致一个可能敏感的文件没有被推送到源代码控制,但仍然包含在 npm 包中。

Another good practice to adopt is making use of the files property in package.json, which works as an allowlist and specifies the array of files to be included in the package that is to be created and installed (while the ignore file functions as a denylist). The files property and an ignore file can both be used together to determine which files should explicitly be included, as well as excluded, from the package. When using both, the files property in package.json takes precedence over the ignore file.

采用另一个良好实践是利用 package.json 中的 files 属性,它作为一个允许列表,指定了要包含在创建和安装的包中的文件数组(而忽略文件则充当禁止列表)。files 属性和忽略文件都可以一起使用,以确定哪些文件应该明确包含在包中,以及排除哪些文件。当两者都使用时,package.json 中的 files 属性优先于忽略文件。

When a package is published, the npm CLI will verbosely display the archive being created. To be extra careful, add a --dry-run command-line argument to your publish command in order to first review how the tarball is created without actually publishing it to the registry.

当发布一个包时,npm CLI 会详细显示正在创建的存档。为了更加小心,请在发布命令中添加 --dry-run 命令行参数,以便首先审查如何创建存档而不实际将其发布到注册表。

For details about revoking access token, see the official documentation: Revoking access tokens.

有关撤销访问令牌的详细信息,请参阅官方文档:撤销访问令牌。

2) Enforce the lockfile / 2) 强制锁文件

We embraced the birth of package lockfiles with open arms, which introduced: deterministic installations across different environments, and enforced dependency expectations across team collaboration. Life is good! Or so I thought… what would have happened had I slipped a change into the project’s package.json file but had forgotten to commit the lockfile alongside of it?

我们热情地拥抱了包锁文件的诞生,它引入了:在不同环境之间进行确定性的安装,并在团队协作中强制执行依赖项期望。生活很美好!或者我以为如此……如果我忘记将更改放入项目的 package.json 文件中,但忘记了提交锁文件,会发生什么呢?

Both Yarn and npm act the same during dependency installation. When they detect an inconsistency between the project’s package.json and the lockfile, they compensate for such change based on the package.json manifest by installing different versions than those that were recorded in the lockfile.

Yarn 和 npm 在依赖项安装期间表现相同。当它们检测到项目 package.json 和锁文件之间的不一致时,会根据 package.json 清单安装与锁文件中记录的不同版本来补偿这种变化。

This kind of situation can be hazardous for build and production environments as they could pull in unintended package versions and render the entire benefit of a lockfile futile.

这种情况对构建和生产环境可能是有害的,因为它们可能会拉入意外的包版本,从而使锁文件的全部好处化为乌有。

Luckily, there is a way to tell both Yarn and npm to adhere to a specified set of dependencies and their versions by referencing them from the lockfile. Any inconsistency will abort the installation. The command-line should read as follows:

幸运的是,有一种方法可以让 Yarn 和 npm 遵守从锁文件中引用的特定依赖项及其版本。任何不一致都将终止安装。命令行应如下所示:

  • If you’re using Yarn, run yarn install --frozen-lockfile.
    中文:如果您使用Yarn,请运行 yarn install --frozen-lockfile。
  • If you’re using npm run npm ci.
    中文:如果您使用npm,请运行 npm ci。

3) Minimize attack surfaces by ignoring run-scripts / 3) 通过忽略运行脚本最小化攻击面

The npm CLI works with package run-scripts. If you’ve ever run npm start or npm test then you’ve used package run-scripts too. The npm CLI builds on scripts that a package can declare, and allows packages to define scripts to run at specific entry points during the package’s installation in a project. For example, some of these script hook entries may be postinstall scripts that a package that is being installed will execute in order to perform housekeeping chores.

npm命令行界面与包的运行脚本一起工作。如果您曾经运行过 npm start 或 npm test,那么您也已经使用了包的运行脚本。npm命令行界面基于包可以声明的脚本,并允许包在项目安装过程中定义在特定入口点运行的脚本。例如,这些脚本钩子条目中的一些可能是 postinstall 脚本,被安装的包将执行这些脚本以执行一些维护工作。

With this capability, bad actors may create or alter packages to perform malicious acts by running any arbitrary command when their package is installed. A couple of cases where we’ve seen this already happening is the popular eslint-scope incident that harvested npm tokens, and the crossenv incident, along with 36 other packages that abused a typosquatting attack on the npm registry.

利用这种能力,恶意行为者可能会创建或修改包,在他们的包被安装时运行任何任意命令以执行恶意行为。我们已经看到的一些案例包括流行的eslint-scope事件,该事件窃取了npm令牌,以及crossenv事件,以及36个其他滥用域名欺骗攻击的npm注册表包。

Apply these npm security best practices to minimize the malicious module attack surface:

应用以下npm安全最佳实践以最小化恶意模块的攻击面:

  • Always vet and perform due-diligence on third-party modules you install to confirm their health and credibility.
    中文:始终对您安装的第三方模块进行审查和尽职调查,以确认其健康和可信度。
  • Hold-off on upgrading immediately to new versions; allow new package versions some time to circulate before trying them out.
    中文:在升级到新版本之前先稍作等待;在尝试使用之前,让新包版本有时间传播。
  • Before upgrading, make sure to review changelog and release notes for the upgraded version.
    中文:在升级前,请确保查看升级版本的变更日志和发布说明。
  • When installing packages make sure to add the --ignore-scripts suffix to disable the execution of any scripts by third-party packages.
    中文:在安装包时,请确保添加 --ignore-scripts 后缀以禁用第三方包中任何脚本的执行。
  • Consider adding ignore-scripts=true to your .npmrc project file, or to your global npm configuration.
    中文:考虑将 ignore-scripts=true 添加到您的 .npmrc 项目文件或全局 npm 配置中。

Using an allowlist for lifecycle scripts / 使用生命周期脚本的允许列表

Disabling lifecycle scripts by default by adding ignore-scripts=true to your .npmrc file is the safest option. If you use packages that rely on lifecycle scripts for legitimate reasons, you can use a plugin like @lavamoat/allow-scripts to create an allowlist of packages authorized to run lifecycle scripts.

通过在 .npmrc 文件中添加 ignore-scripts=true 默认禁用生命周期脚本是最安全的选项。如果您使用依赖生命周期脚本的包,出于合法原因,可以使用类似 @lavamoat/allow-scripts(https://github.com/LavaMoat/LavaMoat/tree/main/packages/allow-scripts)的插件来创建一个授权运行生命周期脚本的 允许列表。

Here's how the allowlist would look like in the package.json file on a project using the popular image processing package sharp:

以下是在使用流行的图像处理包 sharp 的项目中,package.json 文件中允许列表的示例:

{
  "lavamoat": {
    "allowScripts": {
      "sharp": true
    }
  }
}

4) Assess npm project health / 4) 评估 npm 项目健康

npm outdated command / npm outdated 命令

Rushing to constantly upgrade dependencies to their latest releases is not necessarily a good practice if it is done without reviewing release notes, the code changes, and generally testing new upgrades in a comprehensive manner. With that said, staying out of date and not upgrading at all, or after a long time, is a source for trouble as well.

不断急于升级依赖到最新版本,如果没有审查发布说明、代码更改以及全面测试新升级,这并不一定是好的做法。话虽如此,长时间不更新或不升级同样也是问题的来源。

The npm CLI can provide information about the freshness of dependencies you use with regards to their semantic versioning offset. By running npm outdated, you can see which packages are out of date. Dependencies in yellow correspond to the semantic versioning as specified in the package.json manifest, and dependencies colored in red mean an update is available. Furthermore, the output also shows the latest version for each dependency.

npm CLI 可以提供有关您使用的依赖项与其语义版本偏移的新鲜度的信息。通过运行 npm outdated,您可以看到哪些包已经过时。黄色表示的依赖项对应于 package.json 清单中指定的语义版本,而红色表示的依赖项意味着有可用的更新。此外,输出还会显示每个依赖项的最新版本。

npm doctor command / npm doctor 命令

Between the variety of Node.js package managers and different versions of Node.js you may have installed in your path, how do you verify a healthy npm installation and working environment? Whether you’re working with the npm CLI in a development environment or within a CI, it is important to assess that everything is working as expected.

在您路径中安装的各种 Node.js 包管理器和不同的 Node.js 版本之间,您如何验证 npm 的健康安装和工作环境?无论您是在开发环境中使用 npm CLI,还是在 CI 中,评估一切是否按预期工作都至关重要。

Call the doctor! The npm CLI incorporates a health assessment tool to diagnose your environment for a well-working npm interaction. Run npm doctor to review your npm setup:

呼叫医生!npm CLI 集成了健康评估工具,用于诊断您的环境以确保 npm 交互正常。运行 npm doctor 来审查您的 npm 设置:

  • Check the official npm registry is reachable, and display the currently configured registry.
    中文:检查官方 npm 注册表是否可达,并显示当前配置的注册表。
  • Check that Git is available.
    中文:检查 Git 是否可用。
  • Review installed npm and Node.js versions.
    中文:审查已安装的 npm 和 Node.js 版本。
  • Run permission checks on the various folders such as the local and global node_modules, and on the folder used for package cache.
    中文:在本地和全局 node_modules 以及用于包缓存的文件夹上运行权限检查。
  • Check the local npm module cache for checksum correctness.
    中文:检查本地 npm 模块缓存以验证校验和的正确性。

5) Audit for vulnerabilities in open source dependencies / 5) 审计开源依赖中的漏洞

The npm ecosystem is the single largest repository of application libraries among all the other language ecosystems. The registry and the libraries in it are at the core for JavaScript developers as they are able to leverage work that others have already built and incorporate it into their codebase. With that said, the increasing adoption of open source libraries in applications brings with it an increased risk of introducing security vulnerabilities.

npm 生态系统是所有其他语言生态系统中最庞大的应用程序库存储库。该注册表及其中的库是 JavaScript 开发者的核心,因为他们能够利用他人已经构建的工作并将其纳入自己的代码库。因此,随着在应用程序中采用开源库的增加,引入安全漏洞的风险也随之增加。

Many popular npm packages have been found to be vulnerable and may carry a significant risk without proper security auditing of your project’s dependencies. Some examples are npm request, superagent, mongoose, and even security-related packages like jsonwebtoken, and validator.

许多流行的 npm 包被发现存在漏洞,并且在没有对项目依赖进行适当安全审计的情况下可能存在重大风险。例如,npm request,superagent,mongoose,甚至与安全相关的包如 jsonwebtoken 和 validator。

Security doesn’t end by just scanning for security vulnerabilities when installing a package but should also be streamlined with developer workflows to be effectively adopted throughout the entire lifecycle of software development, and monitored continuously when code is deployed:

安全并不仅仅在安装包时扫描安全漏洞,而应该与开发工作流程相结合,以在整个软件开发生命周期中有效采用,并在代码部署时持续监控:

6) Artifact governance and supply chain protections / 6) 艺术品治理和供应链保护

Use a local npm proxy / 使用本地 npm 代理

The npm registry is the biggest collection of packages that is available for all JavaScript developers and is also the home of most Open Source projects for web developers. But sometimes you might have different needs in terms of security, deployments or performance. When this is true, npm allows you to switch to a different registry:

npm仓库是所有JavaScript开发者可用的最大包集合,也是大多数Web开发者的开源项目的家园。但有时您可能在安全性、部署或性能方面有不同的需求。当这种情况发生时,npm允许您切换到不同的仓库:

When you run npm install, it automatically starts a communication with the main registry to resolve all your dependencies; if you wish to use a different registry, that too is pretty straightforward:

当您运行npm install时,它会自动与主仓库进行通信以解析所有依赖项;如果您想使用不同的仓库,这也很简单:

  • Set npm set registry to set up a default registry.
    中文:使用npm set registry来设置默认仓库。
  • Use the argument --registry for one single registry.
    中文:使用--registry参数来指定单个仓库。

Verdaccio is a simple, lightweight zero-config-required private registry and installing it is as simple as follows: $ npm install --global verdaccio.

Hosting your own registry was never so easy! Let’s check the most important features of this tool:

托管自己的仓库从未如此简单!让我们来看看这个工具最重要的特性:

  • It supports the npm registry format including private package features, scope support, package access control and authenticated users in the web interface.
    中文:它支持npm仓库格式,包括私有包功能、作用域支持、包访问控制和网页界面中的认证用户。
  • It provides capabilities to hook remote registries and the power to route dependencies to different registries and cache their tarballs. To reduce duplicate downloads and save bandwidth in your local development and CI servers, you should proxy all dependencies.
    中文:它提供了挂钩远程仓库的能力,可以将依赖项路由到不同的仓库并缓存它们的tar包。为了减少重复下载并在本地开发和CI服务器上节省带宽,您应该代理所有依赖项。
  • As an authentication provider it uses htpasswd security by default, but also supports GitLab, Bitbucket, and LDAP. You can also use your own.
    中文:作为认证提供者,它默认使用htpasswd安全机制,但也支持GitLab、Bitbucket和LDAP。您也可以使用自己的认证方式。
  • It’s easy to scale using a different storage provider.
    中文:使用不同的存储提供者可以轻松进行扩展。
  • If your project is based in Docker, using the official image is the best choice.
    中文:如果您的项目基于Docker,使用官方镜像是最好的选择。
  • It enables really fast bootstrap for testing environments, and is handy for testing big mono-repo projects.
    中文:它为测试环境提供了快速的初始化,对于测试大型单仓库项目也非常方便。

Governance & Verification Steps / 治理与验证步骤

Supply-chain attacks increasingly target build artifacts, registries and CI credentials. Add lightweight governance and verification steps to reduce risk and improve response time:

供应链攻击越来越多地针对构建工件、注册表和CI凭证。添加轻量级的治理和验证步骤以降低风险并提高响应时间:

  • Track provenance and produce an SBOM for builds (CycloneDX/SPDX) so you can trace what was built and where inputs originated.
    中文:跟踪来源并生成构建的SBOM(CycloneDX/SPDX),以便您可以追踪构建的内容以及输入的来源。

CycloneDX Example:

CycloneDX 示例:

# Generate SBOM
npm install @cyclonedx/cyclonedx-npm
npx @cyclonedx/cyclonedx-npm --validate > sbom.json # Use the flag `--omit dev` to exclude dev dependencies from SBOM if needed
  • Sign artifacts and build provenance (for example, use Sigstore / cosign or similar signing tools) so consumers can verify integrity before installing.
    中文:签名工件和构建来源(例如,使用Sigstore / cosign或类似签名工具),以便消费者在安装前验证完整性。

Illustrative Sigstore 5 verification example, assuming a GitHub Actions signing workflow. Replace the placeholder repository and workflow with your trusted release policy. Verify both the expected issuer and certificate identity; identity patterns are regular expressions, so anchor them and escape literal dots. Never derive this policy from the untrusted bundle.

以下是一个Sigstore 5验证示例,假设使用GitHub Actions签名工作流。将占位符仓库和工作流替换为您的受信任发布策略。验证预期的发行者和证书身份;身份模式是正则表达式,因此请将其锚定并转义字面点。永远不要从不受信任的包中派生此策略。

// sign-and-verify.mjs
// npm install sigstore@5

import * as fs from 'fs';
import * as sigstore from 'sigstore';

// Path to your built npm package (via `npm pack`)
const artifact = 'my-lib-1.0.0.tgz';

// --- Sign ---
const payload = fs.readFileSync(artifact);
const bundle = await sigstore.sign(payload);
fs.writeFileSync(`${artifact}.sigstore.json`, JSON.stringify(bundle, null, 2));
console.log('Signed:', artifact);

// --- Verify ---
await sigstore.verify(bundle, payload, {
  certificateIssuer: 'https://token.actions.githubusercontent.com',
  certificateIdentityURI:
    '^https://github\\.com/OWNER/REPOSITORY/\\.github/workflows/release\\.yml@refs/heads/main$',
});
console.log('Verified OK!');
  • Prefer immutable, access-controlled registries or vetted mirrors (private registries, Verdaccio with an upstream cache, or approved mirrors) and enable retention / immutability policies where available.
    中文:优先选择不可变、受控访问的注册表或经过审查的镜像(私有注册表、带有上游缓存的Verdaccio或批准的镜像),并在可能的情况下启用保留/不可变性策略。
  • Restrict, scope and rotate CI and publisher tokens. Bind publisher tokens to workflows or IP ranges and minimize privileges.
    中文:限制、范围和轮换 CI 和发布者令牌。将发布者令牌绑定到工作流或 IP 范围,并最小化权限。
  • Verify packages during CI: check signatures or provenance, validate the SBOM, run SCA and static analysis, and install from pinned lockfile resolutions.
    中文:在 CI 期间验证软件包:检查签名或来源,验证 SBOM,运行 SCA 和静态分析,以及从固定的锁定文件解析中安装。
  • Automate monitoring and alerts for unusual publishes, token usage or dependency changes and keep a documented remediation playbook (revoke tokens, deprecate/yank compromised packages, publish fixes and notify consumers).
    中文:自动监控和警报异常发布、令牌使用或依赖项变更,并保持文档化的修复剧本(撤销令牌、弃用/撤回受损害的包、发布修复并通知消费者)。

These measures are incremental and low-risk to adopt. Combined they make supply-chain attacks harder and speed up identification and recovery if a compromise occurs.

这些措施是渐进的且风险较低,结合使用可以使供应链攻击更难,并在发生妥协时加快识别和恢复速度。

7) Responsibly disclose security vulnerabilities / 7) 负责任地披露安全漏洞

When security vulnerabilities are found, they pose a potentially serious threat if they are publicised without prior warning or appropriate remedial action for users who cannot protect themselves.

当发现安全漏洞时,如果没有事先警告或为无法自我保护的用户采取适当的补救措施,公开这些漏洞可能会造成潜在的严重威胁。

It is recommended that security researchers follow a responsible disclosure program, which is a set of processes and guidelines that aims to connect the researchers with the vendor or maintainer of the vulnerable asset, in order to convey the vulnerability, its impact and applicability. Once the vulnerability is correctly triaged, the vendor and researcher coordinate a fix and a publication date for the vulnerability in an effort to provide an upgrade-path or remediation for affected users before the security issue is made public.

建议安全研究人员遵循负责任的披露计划,这是一个旨在将研究人员与易受攻击资产的销售商或维护者联系起来的流程和指南,以便传达漏洞、其影响和适用性。一旦漏洞得到正确分类,供应商和研究人员将协调修复和漏洞发布日期,以便在安全问题公开之前为受影响的用户提供升级路径或补救措施。

8) Enable 2FA / 8) 启用双因素认证

Enabling two-factor authentication (2FA) is a critical npm security best practice. npm requires 2FA or a granular access token configured to bypass 2FA for package publishing. For interactive publishing, require 2FA and disallow token-based publishing where practical.

启用双因素认证(2FA)是 npm 安全的最佳实践。npm 要求 2FA 或配置为绕过 2FA 的细粒度访问令牌来发布软件包。对于交互式发布,要求启用 2FA 并在实际可行的情况下禁止基于令牌的发布。

npm supports two 2FA modes for an account:

npm支持两种双因素认证模式:

  • Authorization-only—when a user logs in to npm via the website or the CLI, or performs other sets of actions such as changing profile information.
    中文:授权模式——当用户通过网站或CLI登录npm,或执行更改个人资料信息等操作时。
  • Authorization and write-mode—profile and log-in actions, as well as write actions such as managing tokens and packages, and minor support for team and package visibility information.
    中文:授权和写入模式——包括个人资料和登录操作,以及管理令牌和包等写入操作,以及团队和包可见性信息的有限支持。

To get started, see the official documentation for configuring 2FA and requiring 2FA for package publishing.

要开始使用,请参阅官方文档中的配置双因素认证和要求为包发布启用双因素认证。

Equip yourself with an authentication application, such as Google Authenticator, which you can install on a mobile device, and you’re ready to get started. One easy way to get started with the 2FA extended protection for your account is through npm’s user interface, which allows enabling it very easily. If you’re a command-line person, it’s also easy to enable 2FA when using a supported npm client version (>=5.5.1):

使用认证应用程序,例如Google Authenticator,您可以在移动设备上安装它,然后就可以开始使用了。为您的账户启用2FA扩展保护的一个简单方法是通过npm的用户界面,它允许非常容易地启用它。如果您是命令行用户,在支持npm客户端版本(>=5.5.1)时启用2FA也很简单:

npm profile enable-2fa auth-and-writes

Follow the command-line instructions to enable 2FA, and to save emergency authentication codes. If you wish to enable 2FA mode for login and profile changes only, you may replace the auth-and-writes with auth-only in the code as it appears above.

按照命令行说明启用2FA,并保存紧急认证代码。如果您只想为登录和个人资料更改启用2FA模式,可以在上述代码中将auth-and-writes替换为auth-only。

Additional Security Resources / 其他安全资源

9) Minimize npm Access Token Use

Classic npm tokens have been revoked. Prefer trusted publishing with OpenID Connect for supported CI/CD publishing workflows because it avoids stored, long-lived publishing credentials.

When a token is still required, use a granular access token with only the packages, organizations, and permissions needed. Use read-only access for dependency installation, set the shortest practical expiration, restrict source IP ranges where stable, and enable bypassing 2FA only for non-interactive workflows that cannot use trusted publishing. Store the token in a CI secret store and never commit it.

Review tokens regularly with npm token list and immediately revoke unnecessary or exposed tokens with npm token revoke <token-id>.

10) Understanding typosquatting and slopsquatting attacks

Typosquatting attacks

Typosquatting is an attack that relies on mistakes made by users, such as typos. With typosquatting, bad actors publish malicious modules to the npm registry with names that look much like existing popular modules. These malicious packages exploit common typing errors or visual similarities to trick developers into installing them instead of the legitimate packages they intended to use.

The Snyk security team has tracked tens of malicious packages in the npm ecosystem that used typosquatting to trick users into installing them; similar attacks have been observed on the PyPi Python registry as well. Some of the most notable incidents include cross-env, event-stream, and eslint-scope.

Snyk安全团队已追踪到npm生态系统中的数十个恶意软件包,这些软件包利用拼写欺骗手段诱骗用户安装;类似的攻击也已在PyPi Python注册表中观察到。其中一些最引人注目的案例包括cross-env、event-stream和eslint-scope。

One of the main targets for typosquatting attacks are user credentials, since any package has access to environment variables via the global variable process.env. Other examples include the event-stream case, where attackers targeted developers in the hopes of injecting malicious code into an application's source code.

拼写欺骗攻击的主要目标之一是用户凭证,因为任何软件包都可以通过全局变量process.env访问环境变量。其他例子包括event-stream案例,攻击者针对开发者,希望将恶意代码注入应用程序的源代码中。

Slopsquatting attacks / 拼写欺骗攻击

Slopsquatting is a newer attack vector that exploits AI coding assistants. When developers ask AI tools like ChatGPT or GitHub Copilot to suggest packages, these models may hallucinate package names that do not actually exist. Attackers monitor these hallucinations and publish malicious packages with those exact names, knowing developers may blindly trust and install AI-suggested packages.

拼写欺骗是一种较新的攻击向量,它利用了AI编码助手。当开发者请求AI工具如ChatGPT或GitHub Copilot建议软件包时,这些模型可能会产生不存在的软件包名称。攻击者会监视这些幻觉,并发布具有那些确切名称的恶意软件包,知道开发者可能会盲目信任并安装AI建议的软件包。

Unlike typosquatting which exploits human typing errors, slopsquatting exploits AI's tendency to generate plausible-looking but non-existent package names — making it harder to detect since the suggested name looks completely legitimate.

与利用人类打字错误的拼写欺骗不同,拼写欺骗利用了AI生成看似合理但实际上不存在的软件包名称的倾向——这使得它更难检测,因为建议的名称看起来完全合法。

For example, an AI assistant might suggest node-fetch-promise instead of the real package node-fetch. If an attacker has already published a malicious package under that hallucinated name, installing it compromises your system silently.

例如,一个AI助手可能会建议node-fetch-promise而不是真正的软件包node-fetch。如果攻击者已经以那个幻觉的名称发布了恶意软件包,安装它将默默地损害您的系统。

To protect against slopsquatting:

为了防止拼写欺骗:

  • Run npm view <package-name> before installing any AI-suggested package to confirm it exists.
    中文:在安装任何AI建议的软件包之前,运行npm view <软件包名称>以确认它是否存在。
  • Check the package download count — legitimate packages have thousands or millions of downloads while newly published malicious ones have very few.
    中文:检查包的下载次数——合法的包通常有数千或数百万次下载,而新发布的恶意包下载量很少。
  • Verify the package has a real GitHub repository with genuine code, commits and contributors.
    中文:验证包是否有一个真实的GitHub仓库,包含真实的代码、提交和贡献者。
  • Be suspicious of packages created very recently with no release history.
    中文:对于最近创建且没有发布历史的包,要保持怀疑态度。
  • Never blindly run npm install on packages suggested by AI tools without independent verification.
    中文:在未经独立验证的情况下,切勿盲目运行由AI工具推荐的npm install命令。
  • In team environments, add npm view <package-name> as a CI check for any new AI-suggested dependencies before they reach production.
    中文:在团队环境中,在将任何AI推荐的依赖项部署到生产之前,将npm view <package-name>添加为CI检查。

11) Use trusted publishers for secure package publishing / 11) 使用受信任的发布者进行安全包发布

Traditional npm publishing relies on long-lived tokens that can be compromised or accidentally exposed. Trusted publishing with OpenID Connect (OIDC) provides a more secure alternative by using short-lived, workflow-specific credentials that are automatically generated during CI/CD processes. Check the official trusted publishing documentation for the current supported providers and runner restrictions.

传统的npm发布依赖于长期有效的令牌,这些令牌可能被泄露或意外暴露。使用OpenID Connect (OIDC) 的受信任发布提供了一种更安全的替代方案,它通过在CI/CD过程中自动生成短期、特定于工作流程的凭证来实现。请查看官方受信任发布文档,了解当前支持的提供者和运行限制。

How trusted publishing works / 受信任发布的工作原理

Trusted publishing creates a trust relationship between npm and your CI/CD provider using OIDC. When you configure a trusted publisher for your package, npm will accept publishes from the specific workflow you've authorized, in addition to traditional authentication methods like npm tokens and manual publishes. The npm CLI automatically detects OIDC environments and uses them for authentication before falling back to traditional tokens.

通过信任发布,npm 使用 OIDC 与您的 CI/CD 提供商建立信任关系。当您为您的包配置信任发布者时,npm 将接受您已授权的特定工作流程的发布,除了传统的认证方法,如 npm 令牌和手动发布。npm CLI 会自动检测 OIDC 环境,并在回退到传统令牌之前用于认证。

This approach eliminates the security risks associated with long-lived write tokens, which can be compromised, accidentally exposed in logs, or require manual rotation. Instead, each publish uses short-lived, cryptographically-signed tokens that are specific to your workflow and cannot be extracted or reused.

这种方法消除了与长期存在的写入令牌相关的安全风险,这些令牌可能被泄露、意外暴露在日志中或需要手动轮换。相反,每次发布都使用短暂且经过密码学签名的令牌,这些令牌特定于您的工作流程,无法提取或重复使用。

Automatic provenance generation / 自动来源生成

When publishing via trusted publishing, npm automatically generates provenance attestations that provide cryptographic proof of package authenticity. This helps users verify that packages come from legitimate sources and haven't been tampered with.

通过信任发布进行发布时,npm 会自动生成来源证明,提供对包真实性的密码学证明。这有助于用户验证包来自合法来源,且未被篡改。

For more information, see the npm trusted publishing documentation.

有关更多信息,请参阅 npm 信任发布文档。

12) Prevent dependency confusion attacks / 12) 防止依赖混淆攻击

A dependency confusion attack occurs when an attacker publishes a malicious package on the public npm registry using the same name as your internal private package, but with a higher version number. When you run npm install, npm may resolve the public malicious package instead of your internal one because of the higher version.

依赖混淆攻击发生在攻击者在公共 npm 注册表中发布恶意包时,使用与您的内部私有包相同的名称,但版本号更高。当您运行 npm install 时,npm 可能会解析公共恶意包而不是您的内部包,因为版本号更高。

Attackers typically discover internal package names through:

攻击者通常通过以下方式发现内部包名称:

  • Leaked package.json files accidentally pushed to public GitHub repositories
    中文:意外推送到公共GitHub仓库的package.json文件
  • Job postings that mention internal tools or package names
    中文:提及内部工具或包名的职位发布
  • Error messages or stack traces that reveal internal dependency names
    中文:泄露内部依赖名称的错误消息或堆栈跟踪

To protect against dependency confusion:

为了防止依赖混淆:

  • Always use scoped package names for internal packages (e.g., @yourorg/package-name instead of package-name)
    中文:始终为内部包使用作用域包名(例如,@yourorg/package-name而不是package-name)。
  • Configure your .npmrc to explicitly point scoped packages to your private registry by setting @yourorg:registry=https://your-private-registry.example.com
    中文:配置你的.npmrc以显式地将作用域包指向你的私有注册表,通过设置@yourorg:registry=https://your-private-registry.example.com。
  • Reserve your internal package names on the public npm registry by publishing an empty placeholder to prevent attackers from claiming them.
    中文:通过发布一个空占位符,在公共npm注册表中保留您的内部包名,以防止攻击者声称它们。

13) Verify documentation examples before copying into production / 13) 在复制到生产环境之前验证文档示例

Library README files and official examples are often copied directly into production code. While the libraries themselves may use secure defaults internally, their documentation examples sometimes demonstrate insecure patterns that undermine those very defaults. This creates a "documentation attack surface" — a class of vulnerability where the security risk comes not from the library's code, but from how its documentation teaches developers to use it.

库的README文件和官方示例通常直接复制到生产代码中。虽然库本身可能使用内部的安全默认值,但它们的文档示例有时会展示不安全的模式,这些模式会破坏那些默认值。这创建了一个“文档攻击面”——一类漏洞,其中安全风险不是来自库的代码,而是来自其文档教导开发者如何使用它的方式。

The pattern / 该模式

A library implements strong security internally but its README examples use weaker configurations for brevity or backward compatibility. Developers copy these examples verbatim, unknowingly introducing vulnerabilities that the library was designed to prevent. Unlike supply chain attacks, these vulnerabilities pass every audit tool because the library code itself is safe — only the copy-pasted usage pattern is insecure.

一个库在内部实现了强大的安全性,但其README示例为了简洁或向后兼容性使用了较弱的配置。开发者直接复制这些示例,无意中引入了库设计旨在预防的安全漏洞。与供应链攻击不同,这些漏洞可以通过每个审计工具,因为库代码本身是安全的——只有复制的使用模式是不安全的。

Real-world examples / 现实世界示例

This pattern has been documented across popular npm packages with combined weekly downloads exceeding 195 million:

这种模式已在流行的npm包中得到记录,每周下载量超过1.95亿次:

  • Weak key derivation: A widely-used encryption library's README examples derive AES keys from passphrases using MD5 with a single iteration (EVP_BytesToKey), allowing a GPU to test billions of candidate passphrases per second. The library's own PBKDF2 module uses stronger defaults, but the prominent AES examples do not use it.
    中文:弱密钥派生:一个广泛使用的加密库的README示例使用MD5进行单次迭代(EVP_BytesToKey)从密码短语派生AES密钥,允许GPU每秒测试数十亿个候选密码短语。库自己的PBKDF2模块使用更强的默认值,但突出的AES示例没有使用它。
  • Credential exposure on redirect: An HTTP client library's README demonstrates a beforeRedirect callback that re-injects authorization headers after the library has already stripped them during protocol downgrades (HTTPS to HTTP), effectively bypassing the library's own security mechanism.
    中文:重定向中的凭证泄露:一个HTTP客户端库的README演示了一个beforeRedirect回调,在库在协议降级(HTTPS到HTTP)期间已经移除授权头之后重新注入授权头,有效地绕过了库自己的安全机制。
  • Regex anchoring: Libraries that accept regex patterns for validation (e.g., JWT audience matching, CORS origin matching) show examples with unanchored patterns like /example\.com/, which match malicious-example.com. The fix is ^https:\/\/example\.com$, but the documentation doesn't demonstrate anchoring.
    中文:正则表达式锚定:接受正则表达式进行验证的库(例如,JWT 受众匹配、CORS 原始匹配)展示了未锚定的模式示例,如/example\.com/,它匹配malicious-example.com。修复方法是^https:\/\/example\.com$,但文档没有展示锚定。
  • Insecure randomness: A file upload library's README generates filenames with Math.random() while the library's own default uses crypto.randomBytes(16). Developers who customize the filename — following the README example — downgrade from cryptographic to predictable randomness.
    中文:不安全的随机性:一个文件上传库的README使用Math.random()生成文件名,而库自己的默认值使用crypto.randomBytes(16)。遵循README示例自定义文件名的开发者从加密随机性降级到可预测的随机性。

How to protect yourself / 如何保护自己

  • Never copy README examples into production without a security review. Treat documentation code the same way you treat code from Stack Overflow — as a starting point, not a production-ready solution.
    中文:永远不要在没有安全审查的情况下将README示例复制到生产环境中。将文档代码视为与Stack Overflow上的代码相同——作为一个起点,而不是一个现成的解决方案。
  • Check for secure defaults in the library's source code. If the library internally uses crypto.randomBytes(), PBKDF2, or anchored regex patterns, but the README example uses Math.random(), MD5, or unanchored patterns, prefer the library's internal approach.
    中文:检查库源代码中的安全默认设置。 如果库内部使用 crypto.randomBytes()、PBKDF2 或锚定正则表达式模式,但 README 示例使用 Math.random()、MD5 或非锚定模式,则优先考虑库的内部方法。
  • Validate security-sensitive parameters. When a library accepts patterns, keys, or credentials as input, verify that your usage matches security best practices for that parameter type (anchored regex, authenticated encryption, HTTPS-only credentials).
    中文:验证安全敏感参数。 当库接受模式、密钥或凭证作为输入时,请验证您的使用是否符合该参数类型的最佳安全实践(锚定正则表达式、认证加密、仅 HTTPS 凭证)。
  • Report insecure documentation. If you find a README example that teaches an insecure pattern, file an issue with the maintainer. Documentation vulnerabilities affect every developer who copies the example.
    中文:报告不安全的文档。 如果您发现 README 示例教授了不安全的模式,请向维护者提交问题。文档漏洞会影响所有复制示例的开发者。

For more context on this pattern, see the discussion at the Node.js Security Working Group.

有关此模式的更多上下文,请参阅 Node.js 安全工作组 的讨论。

Final Recommendations / 最终建议

Closing our list of npm security best practices are the following tips to reduce the risk of such attacks:

在 npm 安全最佳实践的列表中,以下提示可降低此类攻击的风险:

  • Be extra-careful when copy-pasting package installation instructions into the terminal. Make sure to verify in the source code repository as well as on the npm registry that this is indeed the package you are intending to install. You might verify the metadata of the package with npm info to fetch more information about contributors and latest versions.
    中文:在终端中复制粘贴包安装说明时格外小心。请确保在源代码存储库以及 npm 注册表中验证这确实是您打算安装的包。您可以使用 npm info 验证包的元数据,以获取有关贡献者和最新版本更多信息。
  • Default to having an npm logged-out user in your daily work routines so your credentials won’t be the weak spot that would lead to easily compromising your account.
    中文:在日常工作中默认使用未登录的 npm 用户,以确保您的凭证不会成为导致账户轻易被攻破的弱点。
  • When installing packages, append the --ignore-scripts to reduce the risk of arbitrary command execution. For example: npm install my-malicious-package --ignore-scripts
    中文:安装软件包时,添加 --ignore-scripts 参数以降低任意命令执行的风险。例如:npm install my-malicious-package --ignore-scripts

References / 参考