---
service: "Publicasta"
schema_version: "1.0"
article_id: 290
title: "当 npm install 变成入口：冷静看待 ChainDrop"
language: "zh"
default_language: "en"
canonical_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09?lang=zh"
json_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09.json?lang=zh"
api_url: "https://publicasta.com/api/public/v1/channels/cybersecurity/articles/chaindrop_shai_hulud_npm_install_secrets_2026_08_09?lang=zh"
channel_url: "https://publicasta.com/api/public/v1/channels/cybersecurity"
channel_articles: "https://publicasta.com/api/public/v1/channels/cybersecurity/articles"
search_url: "https://publicasta.com/api/public/v1/search"
documentation_url: "https://publicasta.com/api-docs#reading-publicasta"
openapi_url: "https://publicasta.com/api-docs/openapi.json"
published_at: "2026-08-09T18:00:40+00:00"
updated_at: "2026-08-09T18:00:40+00:00"
translations:
  - language: "ar"
    html_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09?lang=ar"
    markdown_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09.md?lang=ar"
    json_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09.json?lang=ar"
  - language: "de"
    html_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09?lang=de"
    markdown_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09.md?lang=de"
    json_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09.json?lang=de"
  - language: "en"
    html_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09?lang=en"
    markdown_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09.md?lang=en"
    json_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09.json?lang=en"
  - language: "es"
    html_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09?lang=es"
    markdown_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09.md?lang=es"
    json_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09.json?lang=es"
  - language: "fr"
    html_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09?lang=fr"
    markdown_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09.md?lang=fr"
    json_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09.json?lang=fr"
  - language: "pl"
    html_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09?lang=pl"
    markdown_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09.md?lang=pl"
    json_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09.json?lang=pl"
  - language: "ru"
    html_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09?lang=ru"
    markdown_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09.md?lang=ru"
    json_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09.json?lang=ru"
  - language: "zh"
    html_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09?lang=zh"
    markdown_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09.md?lang=zh"
    json_url: "https://publicasta.com/cybersecurity/chaindrop_shai_hulud_npm_install_secrets_2026_08_09.json?lang=zh"
---

# 当 npm install 变成入口：冷静看待 ChainDrop

> 冷静梳理 Shai-Hulud npm 事件：发生了什么，provenance 的边界在哪里，以及团队如何检查 CI secrets 而不把下载量误当受害者数量。

依赖安装本来应该很无聊。ChainDrop 这一轮围绕 Shai-Hulud 的 npm 供应链事件中，危险恰好出现在这个日常步骤：CI runner 或开发者电脑解析到被污染的 npm 包，包在安装阶段执行 lifecycle script，而这些环境里通常放着发布令牌、云访问权限和构建密钥。

 ![依赖关系图中的警告节点、CI runner 和受保护的密钥](https://publicasta.com/storage/projects/9/pages/290/2026/08/97750d89-bcf8-4d9f-8916-37c6bd3565a3.webp)

 ## 8 月 4 日到 9 日发生了什么

 Aikido、Socket、SafeDep、JFrog、Unit 42、Wiz、Endor Labs 等团队都发布了分析，指出这是一场与 Keyv 和 Cacheable 包族有关的 npm 供应链攻击。Aikido 报告称，最早可见的集群从 `keyv@6.0.0` 的发布路径被滥用开始，并牵涉 `flat-cache`、`file-entry-cache`、`cacheable-request`、`cacheable`、`@cacheable/memory`、`cache-manager` 等包。The Hacker News 后来把这一周概括为具有蠕虫式扩散特征的活动，影响范围已超出最初命名空间。

 不同来源给出的数量不完全一致，这是因为研究人员发布文章时事件仍在变化。JFrog 提到 400 多个受影响包，SafeDep 和 Unit 42 在不同时间给出更高的包或版本计数，Endor Labs 强调某些依赖路径每周下载量巨大。这些数字不能相加，也不能直接当作受害者数量。它们描述的是包、版本或下载规模，不等于每次下载都执行了恶意代码。

 ## 为什么它不是普通恶意包事件

 很多 npm 恶意事件来自拼写相似的假包或几乎没人用的小包。ChainDrop 更麻烦，因为入口包含许多团队会间接获得的 transitive dependencies。开发者可能从未主动选择 `keyv`，却通过框架、缓存层、linter、构建工具或服务端库把它带进项目。此时 lockfile 比人的记忆更可靠：危险版本可能藏在你以为自己安装的包下面好几层。

 这次活动还击中了常见的信任信号。研究人员称，一些恶意版本通过看似合法的 release workflow 发布，并可能显示有效 provenance。Provenance 仍然有价值，但它回答的是窄问题：哪个 workflow、以哪个身份、在什么仓库上下文中生成了包。它不能证明 maintainer 账号没有被控制，不能证明 release commit 经过真实审查，也不能证明安装脚本适合在你的 runner 上执行。

 ## 安装阶段的攻击链

 公开报告描述的链条并不复杂。被污染的版本加入 `preinstall` 之类的生命周期命令，启动 `node setup.mjs`。随后脚本加载混淆后的 JavaScript，研究人员提到过 `Math_Symbol.js`、`math_init.js` 等文件名，并在部分流程中使用 Bun 作为运行时。Bun 本身不是被攻破的组件，只是 payload 使用的工具。

 恶意代码进入 CI 或开发机后，会寻找 npm 和 GitHub tokens、云凭据、Vault 和 Kubernetes 材料、SSH keys、`.env`、Terraform state、Docker 与 Helm 配置，以及 GitHub Actions runner 上短暂出现的 secrets。构建环境特别敏感，因为短生命周期凭据和 OIDC 派生 token 可能正好在 job 期间出现在环境变量或内存中。

 蠕虫式扩散来自发布权限。如果 payload 拿到 npm publishing tokens，就可能发布受害者维护的其他包的感染版本；如果拿到 GitHub access，报告中还描述了仓库改动和持久化表面，例如 VS Code tasks 与 Claude Code settings 相关文件。并非每个文件都会自动执行，但它们说明攻击者想把开发自动化变成下一步入口。

 ## 如何判断自己是否暴露

 先看证据，而不是看口号。按照 Aikido、SafeDep、Socket、JFrog、Unit 42 等来源列出的精确包名和版本，检查 `package-lock.json`、`npm-shrinkwrap.json`、`pnpm-lock.yaml`、`yarn.lock`、Bun lockfile、内部镜像和依赖扫描记录。不要只看直接依赖；这次问题的核心就是传递依赖。

 然后确认相关时间窗口内是否真的执行过 install。CI 日志、构建时间、package-manager cache 创建时间、容器层历史和开发机 shell history，可以区分“lockfile 里出现过”和“代码已经运行过”。如果受影响版本只停留在 lockfile，处理可能限于 pin、更新和记录；如果它在有 secrets 的环境里运行过，就应把该环境视为已暴露。

 不要用“升级到 latest”结束处理。升级只能避免未来安装已知坏版本，不能拿回已经复制走的 token。应隔离 runner 或工作站，保留必要日志，重建临时 runner，检查缓存、环境变量、开发工具和仓库改动。撤销 npm 与 GitHub tokens，检查 maintainers、2FA 和权限，再轮换云密钥、Vault、Kubernetes、SSH 与部署 secrets。

 长期做法是把安装脚本当成特权代码执行。能用 `--ignore-scripts` 的地方就用；确实需要 lifecycle scripts 的包要显式批准；测试 npm 12 相关控制以及 pnpm、Yarn、Bun 的对应策略；为自动安装设置最小发布年龄；保持 runner 可重建；把 install、test、publish 分离。ChainDrop 不是开源失败的证明，而是提醒我们：`npm install` 不是下载，它常常是在钥匙旁边执行代码。
