AxiosnpmSupply Chain SecurityFrontend SecurityIncident Response

Axios 供應鏈投毒事件修復 SOP:時間線、IOC 與 CI 防線

整理 Axios 投毒事件 UTC 時間線、IOC 判讀與修復 SOP,附 sh 檢查腳本、GitHub Actions 與 Jenkins CI gate,協助前端團隊快速應變。

· 5 分鐘閱讀

這篇會把「已確認事實」和「工程推論」分開,讓你用可執行的流程完成判讀、修復與防再發。


前言

axios 是前端與 Node.js 生態最廣泛使用的 HTTP client 之一,一旦出現供應鏈事件,影響不會只停在單一框架,而會擴散到 CI、build agent 與開發者本機。

這次事件最容易踩雷的地方,是團隊把「版本回退」當成結案。實務上,若惡意程式在 install 階段已執行,只有降版並不足夠,還必須做憑證輪替、環境清查與快取隔離。

本文重點不是新聞摘要,而是讓台灣前端團隊能直接採用一套 SOP,在下一次 dependency incident 來時可快速複用。


已確認事實(Facts Only)

以下內容以 2026-03-31 公開資料為準,時間皆為 UTC。

事件與版本事實

  1. 官方社群 thread:axios issue #10604created_at: 2026-03-31T03:00:27Z)明確指出 axios@1.14.1axios@0.30.4 為受影響版本。
  2. npm registry 時間戳:
    • axios@1.14.1: 2026-03-31T00:21:58.168Z
    • axios@0.30.4: 2026-03-31T01:00:57.285Z
  3. 截至 2026-03-31(本稿撰寫時),npm view axios dist-tags 顯示 latest1.14.0
  4. plain-crypto-js@4.2.1 被多方通報為此事件中的惡意關聯依賴;npm registry 顯示其發布時間為 2026-03-30T23:59:12.278Z,目前 latest0.0.1-security.0

需要明確區分的推論

  • 1.14.2 是否為「官方已發佈安全版」:截至本文時間點,不能當成既成事實。
  • 受害範圍是否包含終端使用者:要看你的 pipeline 與 artifact 是否被污染,不能一概而論。

5 分鐘判讀:你現在要先確認什麼?

1. 檢查目前依賴樹(不要只看 package.json)

# npm 專案
npm ls axios plain-crypto-js --all

# pnpm 專案
pnpm ls axios plain-crypto-js --depth Infinity

# Yarn (Berry)
yarn why axios
yarn why plain-crypto-js

重點:不是只看「你宣告了什麼版本」,而是「實際解析安裝了什麼版本」。

2. 用可執行腳本判斷「是否命中 IOC」

// scripts/security/check-compromised-deps.mjs
// 從 stdin 讀取 npm/pnpm 的 JSON 輸出,檢查已知壞版本與關聯 IOC。
import fs from 'node:fs';

const input = fs.readFileSync(0, 'utf8').trim();
if (!input) {
  console.error('[SECURITY] empty dependency graph input');
  process.exit(2);
}

const parsed = JSON.parse(input);
const roots = Array.isArray(parsed) ? parsed : [parsed];
const badAxios = new Set(['1.14.1', '0.30.4']);
const badPlainCrypto = new Set(['4.2.1']);
const hits = [];

function walk(node, trail = node?.name || 'root') {
  if (!node || typeof node !== 'object') return;

  const name = node.name;
  const version = node.version;
  if (name === 'axios' && badAxios.has(version)) {
    hits.push(`${trail} -> axios@${version}`);
  }
  if (name === 'plain-crypto-js' && badPlainCrypto.has(version)) {
    hits.push(`${trail} -> plain-crypto-js@${version}`);
  }

  const deps = node.dependencies;
  if (!deps || typeof deps !== 'object') return;

  // npm: dependencies 是 object;pnpm json 也可能是 object
  for (const [depName, depNode] of Object.entries(deps)) {
    const nextTrail = `${trail} > ${depName}`;
    walk(depNode, nextTrail);
  }
}

for (const root of roots) walk(root);

if (hits.length > 0) {
  console.error('[SECURITY] compromised dependency detected:');
  for (const hit of hits) console.error(`- ${hit}`);
  process.exit(1);
}

console.log('[SECURITY] no known compromised versions found');
# npm
npm ls axios plain-crypto-js --all --json \
  | node scripts/security/check-compromised-deps.mjs

# pnpm
pnpm ls axios plain-crypto-js --depth Infinity --json \
  | node scripts/security/check-compromised-deps.mjs

3. 快速 IOC 檢查(環境層)

# 僅做快速初篩,正式鑑識請交由資安流程
# 已知網路 IOC(社群通報):sfrclak.com:8000
rg -n "sfrclak\.com|plain-crypto-js" ~/.npm/_logs 2>/dev/null || true

若命中 dependency IOC 或網路 IOC,就要走完整修復流程,不要只做降版。


修復與防再發:可落地 SOP

A. Incident Triage(先隔離、先輪替)

  1. 暫停可疑 runner / build agent(避免橫向污染)。
  2. 輪替 npm token、CI secrets、雲端憑證(視為可能外洩)。
  3. 保留必要證據(logs、lockfile、build metadata)再進行清理。

B. Clean Rebuild(分 package manager 執行)

# npm: 在保留 lockfile 的前提下做可重現安裝(先關 scripts)
npm ci --ignore-scripts
npm ls axios plain-crypto-js --all

# pnpm
pnpm install --frozen-lockfile --ignore-scripts
pnpm ls axios plain-crypto-js --depth Infinity

# Yarn Berry(示例)
yarn install --immutable --mode=skip-build
yarn why axios

若 lockfile 已確認污染,再建立「受控重建分支」重產 lockfile,並經 code review 後才合併。

C. CI Gate(阻擋已知壞版本)

# .github/workflows/security-check.yml
name: Security Dependency Check

on:
  pull_request:
  push:
    branches: [main]

jobs:
  dependency-guard:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm

      - name: Install dependencies in safe mode
        run: npm ci --ignore-scripts

      - name: Check compromised dependencies
        run: |
          npm ls axios plain-crypto-js --all --json \
            | node scripts/security/check-compromised-deps.mjs

      - name: Run tests
        run: npm test

上面 YAML 可以直接執行,step 結構是合法的,不會出現 - name / - uses 斷裂問題。

D. Policy Hardening(避免下一次重演)

# npm 最低發布年齡(分鐘)
npm config set min-release-age 1440

再加兩個實務策略:

  • 發佈端採 Trusted Publisher / OIDC,減少長效 token 風險。
  • 依賴更新工具(Dependabot / Renovate)加上 cooldown,不要讓「剛發佈版本」立刻進主幹。

常見問題 / 注意事項

Q1:我已經把 axios 降回 1.14.0,還需要做輪替嗎?

需要。若 install 階段曾執行惡意腳本,降版只處理了「現在的套件狀態」,沒有處理「曾外洩的憑證風險」。

Q2:只檢查 package.json 可以嗎?

不行。事件判讀應以 lockfile 與實際依賴樹為主,因為真正執行的是解析後的 dependency graph。

Q3:--ignore-scripts 應該永久開啟嗎?

建議預設開啟於 CI,一旦有必要 scripts 再建立白名單流程,而不是全域放行。

Q4:這篇的 IOC 會不會過期?

會。IOC 是時間敏感資訊,實務上要搭配你公司 SOC/資安團隊與最新 threat intel 更新。


總結

這次 axios 事件的關鍵教訓,不是「某個套件出事」,而是前端團隊必須把 supply chain security 視為日常工程的一部分。

下一步可以立刻做三件事:

  1. 把本文的判讀腳本放進 repo,讓 CI 每次都自動擋壞版本。
  2. min-release-age 與 dependency cooldown 寫成團隊政策。
  3. 針對這次事件補一份 postmortem,沉澱成你們的標準 incident playbook。

只要流程固定,下次再遇到類似事件,你的反應速度和風險控制會快很多。