2632 字
15 分鐘
請注意,本文編寫於 0 天前。
🐛 NPM 供應鏈安全警訊:熱門套件遭入侵
Cover image for 🐛 NPM 供應鏈安全警訊:熱門套件遭入侵

#一、事件概述

2026 年 5 月,npm 生態系遭遇近年來規模最大的一波供應鏈攻擊:被研究人員命名為 Mini Shai-Hulud 的蠕蟲型惡意程式,在一個月內陸續污染 npm、PyPI 與 Composer 合計逾 502 個套件、1,055 個版本。本文聚焦其中影響最廣的兩起事件:

日期(UTC)事件
2026-05-19Mini Shai-Hulud 蠕蟲掃過 @antv 生態,一小時內污染 639 個套件版本
2026-05-22npm 宣布撤銷所有可繞過 2FA 的細粒度存取權杖(Granular Access Token)

此攻擊活動由資安公司 Socket 歸因至 TeamPCP 威脅行為者,此前曾攻擊 Bitwarden CLI、TanStack、Intercom、Mistral AI 等知名開源專案。

#1.1 蠕蟲攻擊模式

Mini Shai-Hulud 是一隻自我傳播的供應鏈蠕蟲,完整攻擊鏈如下:

  1. 取得維護者帳號:透過釣魚或憑證填充,接管擁有大量套件的 npm 維護者帳號
  2. 初始感染:在該帳號的套件中注入惡意 payload,bump 版本後重新發布
  3. 在安裝時執行:惡意版本被下載後,透過 package.jsonpreinstall 鉤子執行 Bun 惡意腳本
  4. 竊取憑證:蒐集 CI/CD 環境中的 npm token、GitHub token、雲端密鑰等機密
  5. 蠕蟲傳播:用竊取的 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-reactECharts React 封裝~110 萬
@antv/g2視覺化文法圖表庫高流量
@antv/g2plot開箱即用圖表庫高流量
@antv/g渲染引擎高流量
@antv/f2行動端圖表高流量
@antv/g6圖(Graph)視覺化高流量
@antv/s2多維分析表格高流量
@antv/x6流程圖引擎高流量
timeago.js相對時間格式化高流量
size-sensorDOM 尺寸監聽高流量
@lint-md/cliMarkdown 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_KEY
  • KUBECONFIG
  • VAULT_TOKEN / VAULT_ADDR
  • STRIPE_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-852bene-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 同步要求所有維護者:

  1. 重新產生 npm 存取權杖
  2. 更新 CI/CD pipeline 中儲存的 secrets
  3. 全面輪換所有可能已外洩的憑證——不僅限於 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 功能的公開預覽,提供更根本的防護機制:

Terminal window
# 暫存發布(送到暫存區,不立即公開)
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 確認是否受影響

Terminal window
# 在專案根目錄,搜尋 @antv 相關套件是否存在可疑版本
# (以 lock file 為準,比 node_modules 更可靠)
grep "@antv" package-lock.json
grep "@antv" pnpm-lock.yaml # pnpm 專案
# 使用 Socket CLI 掃描整個依賴樹的供應鏈風險
npx socket scan create .
# 確認 preinstall 鉤子是否曾被觸發(檢查 npm debug log)
# Windows
type %APPDATA%\npm\_logs\*.log | findstr preinstall
# Linux/macOS
cat ~/.npm/_logs/*.log | grep preinstall

如果你的 CI/CD 日誌中出現 bun run index.js 或對 t.m-kosche.com 的網路請求,請立即視為已入侵,進入事件回應流程。

#4.2 維護者應採取的行動

如果你是 npm 套件維護者,無論是否確認受影響,都應立即執行以下步驟

Terminal window
# 1. 撤銷所有舊的 npm token
npm token list
npm 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 套件使用者應採取的行動

Terminal window
# 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_modules
npm install # 或 pnpm install
# 5. 執行 Socket 供應鏈掃描
npx socket scan create .

#五、防護建議

#5.1 消費端(使用套件的開發者)

  • 啟用 minimumReleaseAge:使用 Renovate 或 Dependabot 時,設定新版本需等待至少 1–3 天才自動升級,給安全社群時間發現問題。pnpm 11 已將此設為預設 1 天。
  • 固定 lock file:CI/CD 中使用 npm cipnpm 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 監控與偵測

Terminal window
# 安裝 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 是最直接可以降低風險的設定——即使面對蠕蟲級別的攻擊速度,多幾天的緩衝期就足以讓安全社群先行示警。

供應鏈安全沒有銀彈,但生態內每個角色各盡一份力,才能讓攻擊的成本逐漸高過收益。


參考資料:

  1. 及時關註安全公告,第一時間響應

這次事件再次提醒我們:開源不等於安全,信任需要驗證。

🐛 NPM 供應鏈安全警訊:熱門套件遭入侵
https://illumi.love/posts/資安向/npm-供應鏈安全警訊熱門套件遭入侵/
作者
𝑰𝒍𝒍𝒖𝒎𝒊糖糖
發布於
2026-05-24
許可協議
🔒CC BY-NC-ND 4.0
分享

如果這篇文章對你有幫助,歡迎分享給更多人!

💬 參與討論
使用 GitHub 帳號登入參與討論