一、事件概述
2026 年 5 月,npm 生態系遭遇近年來規模最大的一波供應鏈攻擊:被研究人員命名為 Mini Shai-Hulud 的蠕蟲型惡意程式,在一個月內陸續污染 npm、PyPI 與 Composer 合計逾 502 個套件、1,055 個版本。本文聚焦其中影響最廣的兩起事件:
| 日期(UTC) | 事件 |
|---|---|
| 2026-05-19 | Mini Shai-Hulud 蠕蟲掃過 @antv 生態,一小時內污染 639 個套件版本 |
| 2026-05-22 | npm 宣布撤銷所有可繞過 2FA 的細粒度存取權杖(Granular Access Token) |
此攻擊活動由資安公司 Socket 歸因至 TeamPCP 威脅行為者,此前曾攻擊 Bitwarden CLI、TanStack、Intercom、Mistral AI 等知名開源專案。
1.1 蠕蟲攻擊模式
Mini Shai-Hulud 是一隻自我傳播的供應鏈蠕蟲,完整攻擊鏈如下:
- 取得維護者帳號:透過釣魚或憑證填充,接管擁有大量套件的 npm 維護者帳號
- 初始感染:在該帳號的套件中注入惡意 payload,bump 版本後重新發布
- 在安裝時執行:惡意版本被下載後,透過
package.json的preinstall鉤子執行 Bun 惡意腳本 - 竊取憑證:蒐集 CI/CD 環境中的 npm token、GitHub token、雲端密鑰等機密
- 蠕蟲傳播:用竊取的 npm token 呼叫 registry API,將同一帳號下所有其他套件也重新感染並發布
蠕蟲的自我傳播設計是此次規模異常龐大的核心原因。
1.2 重要背景:GitHub 資料外洩
2026 年 5 月下旬,GitHub 官方揭露約 3,800 個 GitHub 內部 repository 遭到外洩。調查後確認:入侵路徑是 Nx Console VSCode 擴充套件(v18.95.0,約 220 萬安裝量) 在 TanStack 攻擊波次(5 月 11 日)中被污染,攻擊者藉此竊取 Nx 維護者的 GitHub 憑證,進而滲透 GitHub 內部環境。
二、2026-05-19:@antv 生態供應鏈攻擊
2.1 攻擊概要
攻擊者入侵 npm 維護者帳號 atool(同時也是 @antv 生態的維護者),在 UTC 時間 2026-05-19 01:56 至 02:56 的一小時內,批次污染並發布了 639 個惡意版本,橫跨 323 個套件。
Socket 的自動偵測系統在中位數 6.7 分鐘內識別出首批惡意版本,最終攔截了所有 639 個版本。
2.2 受影響的主要套件
下表列出此波攻擊中幾個代表性套件:
| 套件名 | 說明 | 每週下載量(約) |
|---|---|---|
echarts-for-react | ECharts React 封裝 | ~110 萬 |
@antv/g2 | 視覺化文法圖表庫 | 高流量 |
@antv/g2plot | 開箱即用圖表庫 | 高流量 |
@antv/g | 渲染引擎 | 高流量 |
@antv/f2 | 行動端圖表 | 高流量 |
@antv/g6 | 圖(Graph)視覺化 | 高流量 |
@antv/s2 | 多維分析表格 | 高流量 |
@antv/x6 | 流程圖引擎 | 高流量 |
timeago.js | 相對時間格式化 | 高流量 |
size-sensor | DOM 尺寸監聽 | 高流量 |
@lint-md/cli | Markdown lint 工具 | — |
完整受影響清單以 Socket 安全公告 為準。
2.3 技術分析:惡意 Payload
攻擊者對每個受污染的套件 package.json 注入兩個關鍵修改:
{ "scripts": { "preinstall": "bun run index.js" }, "optionalDependencies": { "@antv/setup": "github:antvis/G2#1916faa..." }}preinstall鉤子:在套件安裝時自動執行,無需任何用戶主動操作optionalDependencies濫用:透過 GitHub 作為分發渠道,繞過 npm registry 的稽核index.js:Bun 執行的主惡意腳本,使用重度 JavaScript 混淆(obfuscation)
惡意腳本的執行流程:
安裝套件 └─ preinstall 觸發 bun run index.js ├─ 呼叫 npm registry API 驗證當前 NPM_TOKEN 是否有寫入權限 ├─ 枚舉該 token 維護者所擁有的所有套件 ├─ 對每個套件:複製惡意 payload → bump patch 版本 → npm publish ├─ 竊取環境變數中的機密(見下方清單) └─ 將竊取資料加密後傳輸至 C2 伺服器目標機密清單:
GITHUB_TOKEN(CI/CD 常見環境變數)AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEYKUBECONFIGVAULT_TOKEN/VAULT_ADDRSTRIPE_KEY- GCP 服務帳號憑證
- Azure 憑證
- SSH 私鑰
.claude/settings.json(Claude AI 工具設定,可能含 API 密鑰)- 其他 AI 開發工具設定
高風險情境:CI/CD pipeline。GitHub Actions / GitLab CI / CircleCI 等環境在執行
npm install時,process.env中通常已注入上述機密,是此攻擊最主要的目標。
2.4 入侵指標(IoCs)
若在你的 CI/CD 環境或系統中發現以下跡象,請立即進行事件回應:
| 類型 | 值 |
|---|---|
| C2 網域 | t.m-kosche.com:443(OTEL 偽裝端點:/api/public/otel/v1/traces) |
| GitHub 外洩 repo 命名規律 | <沙丘詞>-<沙丘詞>-<數字>,例如 sayyadina-stillsuit-852、bene-gesserit-431 |
| 惡意活動標記字串 | niagA oG eW ereH :duluH-iahS(base64/reversed 標記) |
| 惡意 GitHub 帳號 | antvis(冒充官方帳號) |
可疑 optionalDep | @antv/setup 指向 GitHub commit hash 而非 npm 版本 |
三、2026-05-22:npm 撤銷所有細粒度存取權杖
3.1 npm 的回應措施
npm 在 2026-05-22 透過官方 X(Twitter)帳號宣布:
撤銷所有擁有寫入權限且可繞過 2FA 的細粒度存取權杖(Granular Access Tokens)。
此措施旨在切斷 Mini Shai-Hulud 蠕蟲賴以傳播的主要媒介。npm 同步要求所有維護者:
- 重新產生 npm 存取權杖
- 更新 CI/CD pipeline 中儲存的 secrets
- 全面輪換所有可能已外洩的憑證——不僅限於 npm token,也包含 GitHub、AWS、GCP、Azure、SSH、Kubernetes、Vault、Stripe 等
3.2 Staged Publishing(暫存發布)公開預覽
就在撤銷 token 的前兩天(2026-05-20),npm 在 CLI v11.15.0 推出了 Staged Publishing 功能的公開預覽,提供更根本的防護機制:
# 暫存發布(送到暫存區,不立即公開)npm stage publish
# 查看所有待審核的暫存版本npm stage list
# 審核特定版本詳情npm stage view <package>@<version>
# 以 MFA 互動式驗證後核准發布npm stage approve <package>@<version>
# 拒絕並捨棄暫存版本npm stage reject <package>@<version>核心概念:npm publish 不再直接上架,而是先進入暫存區;只有維護者透過 互動式 MFA 驗證 執行 npm stage approve 後,套件才正式公開。由於 approve 指令無法被自動化 token(包含 OIDC Trusted Publishing)執行,即使 CI/CD pipeline 的 token 遭竊,攻擊者也無法完成蠕蟲的自動傳播。
安全研究員 Adnan Khan 表示:
「每個在 NPM 上發布套件的人今天就應該開啟這個功能。」
npm 創辦人 Isaac Schlueter 也公開表示希望未來將非 MFA 發布設為預設關閉。
3.3 Token 撤銷的侷限性
token 撤銷雖然是必要的緊急回應,但並未根本解決問題:
- 蠕蟲仍在活躍:攻擊者可在維護者產生新 token 後,再次透過有效 token 繼續感染
- OIDC Trusted Publishing 並非萬靈丹:TanStack 波次的攻擊者取得了合法的 OIDC trusted-publisher binding,同樣成功發布了惡意版本
- 蠕蟲速度優先:攻擊完成一整波(323 個套件)僅需一小時,人工反應速度遠不及
四、排查與修復
4.1 確認是否受影響
# 在專案根目錄,搜尋 @antv 相關套件是否存在可疑版本# (以 lock file 為準,比 node_modules 更可靠)grep "@antv" package-lock.jsongrep "@antv" pnpm-lock.yaml # pnpm 專案
# 使用 Socket CLI 掃描整個依賴樹的供應鏈風險npx socket scan create .
# 確認 preinstall 鉤子是否曾被觸發(檢查 npm debug log)# Windowstype %APPDATA%\npm\_logs\*.log | findstr preinstall# Linux/macOScat ~/.npm/_logs/*.log | grep preinstall如果你的 CI/CD 日誌中出現 bun run index.js 或對 t.m-kosche.com 的網路請求,請立即視為已入侵,進入事件回應流程。
4.2 維護者應採取的行動
如果你是 npm 套件維護者,無論是否確認受影響,都應立即執行以下步驟:
# 1. 撤銷所有舊的 npm tokennpm token listnpm token revoke <tokenId>
# 2. 產生新的 Automation token(供 CI/CD 使用)npm token create --type=automation
# 3. 若 CI/CD 使用 GitHub Actions,改為 OIDC Trusted Publishing# 在 npmjs.com → Access Tokens → Generate New Token → Granular Access Token# 設定 Organizations: <your-github-org>,不設定 Packages(改用 OIDC)
# 4. 啟用 Staged Publishing(公開預覽階段需手動設定)# 詳見 https://docs.npmjs.com/cli/commands/npm-stage全面輪換的憑證清單:
| 憑證 | 操作 |
|---|---|
| npm Granular Access Token | 撤銷舊 token,重新產生 |
GITHUB_TOKEN / GitHub PAT | 撤銷舊 token,重新產生 |
| AWS Access Key | 停用舊 key,建立新 key,更新 IAM |
| GCP 服務帳號 | 刪除舊 JSON key,產生新 key |
| Azure Service Principal | 重設 client secret |
| Kubernetes Service Account | 輪換 kubeconfig token |
| Vault Token / AppRole | 撤銷並重新核發 |
| Stripe API Key | 撤銷,重新產生 |
| SSH 私鑰 | 撤銷 authorized_keys,重新產生金鑰對 |
4.3 套件使用者應採取的行動
# 1. 確認安裝的 @antv 套件版本(使用 npm ls 或 pnpm list)npm ls @antv/g2 @antv/g2plot echarts-for-react timeago.js
# 2. 若發現可疑版本,強制升級至最新安全版本npm update @antv/g2 @antv/g2plot
# 3. 若有間接依賴,使用 overrides 鎖定版本# 在 package.json 加入:# "overrides": {# "@antv/g2": "^5.2.1",# "echarts-for-react": "^3.0.2"# }
# 4. 重新安裝依賴rm -rf node_modulesnpm install # 或 pnpm install
# 5. 執行 Socket 供應鏈掃描npx socket scan create .五、防護建議
5.1 消費端(使用套件的開發者)
- 啟用
minimumReleaseAge:使用 Renovate 或 Dependabot 時,設定新版本需等待至少 1–3 天才自動升級,給安全社群時間發現問題。pnpm 11 已將此設為預設 1 天。 - 固定 lock file:CI/CD 中使用
npm ci或pnpm install --frozen-lockfile,而非npm install - 定期掃描:將
npx socket scan create .加入 CI pipeline
5.2 發布端(套件維護者)
- 立即啟用 Staged Publishing:npm CLI v11.15.0+ 可用,
npm stage publish取代直接npm publish - 啟用 npm 帳號的 2FA:並選擇最嚴格的「Require 2FA for publishing」設定
- 改用 OIDC Trusted Publishing:減少長效 token 的使用,但注意 OIDC 本身不能取代 MFA 發布驗證
- 審核 CI/CD 的 token 權限:只賦予必要的最小權限,避免一個 token 可以發布多個套件
5.3 監控與偵測
# 安裝 Socket CLI,整合到開發流程npm install -g @socketsecurity/cli
# 掃描專案socket scan create .
# 也可安裝 Socket GitHub App,PR 時自動掃描# https://github.com/apps/socket-security六、結語
Mini Shai-Hulud 事件清楚展示了蠕蟲型供應鏈攻擊的恐怖效率:一個維護者帳號被入侵,一個小時內就污染了超過 300 個套件。npm 的 token 撤銷是必要的,但治標不治本;Staged Publishing 才是更接近根本解法的架構性防護,強烈建議所有 npm 套件維護者在功能穩定後立即啟用。
作為套件使用者,minimumReleaseAge 是最直接可以降低風險的設定——即使面對蠕蟲級別的攻擊速度,多幾天的緩衝期就足以讓安全社群先行示警。
供應鏈安全沒有銀彈,但生態內每個角色各盡一份力,才能讓攻擊的成本逐漸高過收益。
參考資料:
- 及時關註安全公告,第一時間響應
這次事件再次提醒我們:開源不等於安全,信任需要驗證。