正文

npm 供应链攻击最麻烦的地方,不是“装错一个包”这么简单。恶意包通常会在安装阶段自动运行,读取开发机和 CI 环境里的 GitHub token、npm token、SSH key、云厂商凭据、.env、Actions secrets,再把这些凭据拿去继续污染其它包或仓库。

所以一旦项目可能装过被投毒的 npm 包,第一反应不要是 rm -rf node_modulesnode_modules 只是结果之一。你要先确认恶意版本有没有进入 lockfile、有没有在本机或 CI 里执行过生命周期脚本、有没有接触过密钥、有没有把恶意版本缓存进构建环境。

先看几个真实案例

2026-03-30,StepSecurity 披露 Axios npm 包被投毒。攻击者通过被劫持的维护者账号发布了 axios@1.14.1axios@0.30.4,并加入一个伪装成正常加密库的依赖 plain-crypto-js@4.2.1。这个依赖没有被 Axios 源码引用,作用是通过 postinstall 执行跨平台 RAT dropper。StepSecurity 记录称,恶意版本在 npm 上存活了约 2 到 3 小时,GitHub issue axios/axios#10604 也记录了这两个版本被确认 compromised。

这个案例的危险点是:GitHub 仓库源码看起来可能没问题,npm registry 上的发布物却已经被污染。只看 GitHub tag 或源码 diff,不足以判断你是否安全。

2026-05-19,TechRadar 转述多个安全团队的发现:Mini Shai-Hulud 相关攻击在 1 小时内发布了 639 个恶意版本,覆盖 323 个独立 npm 包,目标包括开发者、开源维护者和 CI/CD 环境。报道提到相关攻击会利用被盗登录凭据和 access token 发布恶意包,窃取 GitHub、云和 CI/CD 相关凭据。

2026-06,Red Hat Cloud Services 命名空间下的 npm 包也被报道遭到污染。TechRadar 报道称,攻击者通过被攻陷的 GitHub 账号向多个 npm 包注入恶意内容,目标包括 GitHub Actions secrets、npm tokens、SSH keys 和 cloud credentials。

这些案例说明,npm 投毒已经不只是“浏览器里偷加密货币钱包地址”这种前端风险。现在攻击者更关心开发者工作台和 CI/CD:那里有发布权限、云权限、仓库权限和更多下游项目。

先确认你有没有装过恶意版本

第一步是查 lockfile,而不是只看 package.jsonpackage.json 里可能只有范围版本,真正装过什么要看锁文件。

PowerShell 里先查已知包名和版本:

再查最近一次依赖变更:

重点看这些变化:

如果你使用 pnpm,继续看具体路径和版本:

如果你使用 npm,确认 CI 是否用的是 npm ci。npm 官方文档说明,npm ci 面向 CI 和部署这类自动化环境,要求已有 package-lock.jsonnpm-shrinkwrap.json;如果 lockfile 和 package.json 不匹配,它会报错,而不是更新 lockfile。这能减少 CI 悄悄装到新版本的概率。

新增陌生包可能是被插入的恶意依赖
原包名没变但 resolved URL 变了可能切到异常 registry 或 tarball
integrity 变化说明实际包内容变了
新增 postinstall 相关包安装阶段会自动执行代码
从 registry 包变成 git / tarball 依赖审计和复现更难
PowerShell
Select-String -Path package-lock.json,pnpm-lock.yaml,yarn.lock -Pattern "axios@1.14.1|axios@0.30.4|plain-crypto-js|@tanstack|@mistralai|@redhat-cloud-services" -ErrorAction SilentlyContinue
PowerShell
git diff -- package.json package-lock.json pnpm-lock.yaml yarn.lock
PowerShell
Select-String -Path pnpm-lock.yaml -Pattern "version:|resolution:|integrity:|dependencies:|optionalDependencies:" -Context 0,3

查生命周期脚本和安装时执行

npm 包最危险的入口之一是生命周期脚本:preinstallinstallpostinstallprepare。恶意包常用这些脚本在你安装依赖时自动执行。

先查项目直接依赖里有没有新增脚本:

再查 lockfile 里是否出现可疑包名后,进入临时目录解包查看发布物:

需要看真实 tarball 内容时,用隔离目录,不要在含密钥的项目里直接运行安装:

看到这些内容要暂停:

如果你只是想先让 CI 跑静态检查,可以临时禁用安装脚本:

这不是永久修复。有些合法包需要构建脚本。它的作用是先阻止未知脚本自动运行,让你有机会检查依赖树。

postinstall 下载远程脚本安装阶段就能执行任意代码
混淆 JS / base64 大段字符串可能隐藏下载、执行、外传逻辑
curl / wget / PowerShell 下载可能拉第二阶段 payload
读取 process.env 后发请求可能窃取 CI secret
读取 ~/.ssh.npmrc、云配置可能窃取长期凭据
自删除、替换 package.json可能规避事后检查
PowerShell
Get-Content package.json | Select-String -Pattern '"preinstall"|"install"|"postinstall"|"prepare"'
PowerShell
npm pack <package-name>@<version> --dry-run
PowerShell
mkdir .tmp-npm-check
cd .tmp-npm-check
npm pack <package-name>@<version>
tar -tf .\<package-name>-<version>.tgz
PowerShell
npm ci --ignore-scripts

查本机和 CI 是否接触过密钥

只要恶意包可能执行过,就要按“它能读到当前进程权限范围内的内容”处理。开发机和 CI runner 的风险不一样。

先查本机敏感文件是否可能被安装脚本读取:

不要把这些文件内容贴到聊天、issue 或日志里。这里只确认路径和暴露面。

再查 CI secrets 相关改动:

如果恶意包在 CI 中运行过,并且 workflow 里有 npm token、GitHub token、云凭据或部署密钥,直接进入轮换流程。

本机终端.npmrc、SSH key、Git 凭据、云 CLI 登录、.env
GitHub ActionsGITHUB_TOKEN、Actions secrets、OIDC token、部署密钥
Docker build.npmrc、build args、镜像层、构建缓存
自托管 runner更多本地文件、长期缓存、共享工作目录
发布流水线npm publish token、registry token、GitHub release 权限
PowerShell
Get-ChildItem -Force $HOME -Recurse -File -ErrorAction SilentlyContinue |
  Where-Object {
    $_.FullName -match "\\.npmrc|\\.ssh|\\.aws|gcloud|azure|credentials|config"
  } |
  Select-Object FullName
PowerShell
rg -n "NPM_TOKEN|NODE_AUTH_TOKEN|GITHUB_TOKEN|AWS_|AZURE_|GCP_|PRIVATE_KEY|DEPLOY_KEY|secrets\\." .github workflows .github/workflows

轮换凭据不要只换 npm token

很多开发者看到 npm 包投毒,只会撤销 npm token。但恶意包不一定只偷 npm token。它能读取当前环境里的所有可访问凭据。

按这个顺序处理:

如果你不知道某个 token 是否被读过,按读过处理。供应链事件里,“看起来没有被用”不是安全证明。

npm token立即 revoke,改用短期 granular token 或 Trusted Publishing
GitHub PAT撤销或轮换,检查最近 API 使用记录
GITHUB_TOKEN检查 workflow 权限和可写范围
SSH key轮换 deploy key、机器 key、个人 key
云厂商 key轮换 access key,查 CloudTrail / audit log
registry token轮换私有 registry、GitHub Packages、Verdaccio token
CI secrets更新 secrets,清理旧日志和缓存
.env 里的生产密码轮换数据库、Redis、JWT、Webhook secret

限制 GitHub Actions 权限

npm 投毒经常盯上 CI,因为 CI 里有自动化权限。GitHub 文档建议在 workflow 或 job 级别配置 GITHUB_TOKEN 权限,只给需要的权限。

给普通测试工作流一个保守默认:

发布 workflow 再单独提升权限,不要让所有 job 默认拥有写权限:

npm provenance 文档说明,Trusted Publishing 可以通过 OIDC 生成 provenance attestation,并减少在 CI/CD workflow 里使用长期 access token 的需要。能用 OIDC / Trusted Publishing 的发布流程,优先替代长期 NPM_TOKEN

发布包时也要注意 provenance 的边界:npm 文档明确说 provenance 不能保证包没有恶意代码,它提供的是可验证的源码和构建说明链接。也就是说,provenance 是判断依据,不是免审通行证。

YAML
name: test

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-node@v6
        with:
          node-version: 24
          cache: npm
      - run: npm ci --ignore-scripts
      - run: npm test
YAML
permissions:
  contents: read
  id-token: write

查 npm 发布来源和 provenance

如果你是包维护者,或者你的项目依赖内部包,继续查发布来源。

先看包元数据:

对比几个版本:

异常信号:

能验证签名和 attestations 时,跑:

签名验证失败不等于一定被投毒,可能是生态支持不完整;但对高风险依赖,它足以触发人工复查。

旧版本由 GitHub Actions / Trusted Publisher 发布,新版本由个人 token 发布可能绕过正常发布链路
npm 上有版本,GitHub 没有 tag / commitregistry 发布物和源码不一致
gitHead 缺失或对不上难以追溯源码
新增陌生 maintainer需要确认是否正常授权
发布间隔异常、版本号异常可能是攻击者抢发
PowerShell
npm view <package-name>@<version> version dist.tarball dist.integrity repository gitHead _npmUser
PowerShell
npm view <package-name>@<good-version> _npmUser gitHead dist.integrity
npm view <package-name>@<suspicious-version> _npmUser gitHead dist.integrity
PowerShell
npm audit signatures

不要让 lockfile 自动漂移

供应链攻击最喜欢“自动更新”。如果 CI 每次都根据范围版本重新解析依赖,或者 bot 自动合并 lockfile 变化,恶意版本就更容易进来。

更稳的规则:

pnpm 项目可以在 CI 里使用:

yarn 项目按版本选择:

锁文件不是噪音。供应链事件发生时,lockfile 是你判断“到底装过什么”的证据。

CI 安装npm ci / pnpm install --frozen-lockfile
依赖升级 PR只升级明确包,单独审 lockfile
大版本升级分批升级,不和业务代码混在一个 PR
安全补丁查 advisory、diff、发布来源,再合并
自动合并禁止自动合并带 lifecycle script 变化的依赖
PowerShell
pnpm install --frozen-lockfile
PowerShell
yarn install --immutable

发现可疑依赖后的最小处置流程

发现可疑依赖后,按这个顺序处理。

回退示例:

如果你的项目用 pnpm:

--ignore-scripts 只是应急隔离。确认依赖树安全后,再决定是否恢复脚本执行。

  • 暂停部署和发布流水线。
  • 保留当前 package.json、lockfile、CI run ID、构建日志和镜像 tag。
  • 查本机、CI、容器构建环境是否运行过安装脚本。
  • 回退到确认安全的版本,或临时移除该依赖。
  • 删除 node_modules 后用冻结 lockfile 重装。
  • 轮换可能被读取的 token、SSH key、云凭据和数据库密码。
  • 清理 CI cache、包管理器 cache、Docker build cache 和远程 runner。
  • 检查最近 GitHub Actions、npm publish、registry、云审计日志。
  • 补上 permissions、Trusted Publishing、provenance、安装脚本限制。
  • 记录影响范围和最终安全版本。
PowerShell
git checkout -- package.json package-lock.json pnpm-lock.yaml yarn.lock
Remove-Item -Recurse -Force node_modules
npm ci --ignore-scripts
PowerShell
git checkout -- package.json pnpm-lock.yaml
Remove-Item -Recurse -Force node_modules
pnpm install --frozen-lockfile --ignore-scripts

发布者要减少长期 token

如果你维护 npm 包,攻击面还包括你的发布账号。

优先顺序:

发布前看包里到底有什么:

不要把 .env、测试 token、内部文档、调试脚本、.npmrc、构建缓存发进包。npm 投毒不一定来自外部攻击者;错误发布同样会造成供应链事故。

  • 开启 npm 账号 2FA。
  • 用 Trusted Publishing / OIDC 替代长期发布 token。
  • 如果必须用 token,使用短期、细粒度 token。
  • release workflow 只在 tag / release 事件触发。
  • 发布 job 单独隔离,不和测试 job 共用过多 secrets。
  • npm publish 前跑 npm pack --dry-run 看实际发布内容。
  • 包发布后检查 npm metadata、provenance、GitHub tag 是否一致。
PowerShell
npm pack --dry-run

团队可以固定一张检查表

把这张表放进依赖升级 PR 或安全响应模板里。

npm 供应链安全不是“永远不要装依赖”。真正可执行的做法是:锁住版本、看懂 lockfile、限制安装脚本、减少长期 token、让 CI 权限最小化,并在可疑包执行过时按凭据泄露处理。

只要恶意包已经在本机或 CI 里跑过,清理依赖只是第一步。真正的收尾是把它可能接触过的权限都收回来。

Text
npm supply-chain check:
- [ ] package.json and lockfile diff reviewed
- [ ] no unexpected new transitive package
- [ ] no unexpected git / tarball / remote dependency
- [ ] lifecycle scripts reviewed
- [ ] install tested with frozen lockfile
- [ ] CI job runs with least GITHUB_TOKEN permissions
- [ ] npm token is not available to normal test jobs
- [ ] publish uses Trusted Publishing / OIDC where possible
- [ ] npm audit signatures checked for high-risk release
- [ ] CI cache and Docker cache reviewed if suspicious package ran
- [ ] affected tokens and secrets rotated if install script may have run

参考来源

axios Compromised on npm - Malicious Versions Drop Remote Access TrojanStepSecurityaxios@1.14.1 and axios@0.30.4 are compromisedaxios GitHub IssueMini Shai-Halud hackers publish over 600 compromised npm packagesTechRadarCompromised Red Hat npm packages downloaded over 80,000 times in one weekTechRadarnpm cinpm DocsGenerating provenance statementsnpm DocsUse GITHUB_TOKEN for authentication in workflowsGitHub Docs

相关文章

AI Agent 让你安装依赖时,哪些包不能直接允许智能编程 / 约 12 分钟Docker 镜像里最容易泄露什么:.env、构建缓存、SSH key 和多阶段构建工程实践 / 约 12 分钟Vibe Coding 小应用上线前要查哪些安全坑智能编程 / 约 12 分钟AI Agent 已经能参与攻击链:Claude / Codex 使用前要限制哪些权限智能编程 / 约 10 分钟MCP Server 安装前怎么检查 tool poisoning 风险智能编程 / 约 12 分钟AI Agent 权限怎么设:哪些命令能自动跑,哪些必须人工确认智能编程 / 约 13 分钟AI Coding 的下一步:从 prompt 技巧到工程约束智能编程 / 约 18 分钟装过 codexui-android 后,Codex 登录凭据可能泄露怎么办错误日志 / 约 8 分钟

作者信息