即時対応が必要な方へ: Part 5: 自分のマシンを棚卸しする に2分で実行できるスキャンコマンドがあります。まずそこから始めてください。
2026-04-01 17:03 JST 追記
非エンジニア向けにスキャンツールを作成して公開しました。Claude や ChatGPT などで対話しながら設定していくための INSTRUCTION.md も同梱しています。
はじめに
2026年3月19日〜31日の約2週間で、npm および PyPI エコシステムの広く使われているパッケージが連鎖的に侵害された。
- axios(npm、週間約1億ダウンロード)— メンテナーアカウント乗っ取りによる RAT 配布
- LiteLLM(PyPI、月間約9,700万ダウンロード)— CI/CD クレデンシャル窃取からの連鎖侵害
- Trivy / KICS(GitHub Actions)— バージョンタグの上書き
- Telnyx(PyPI)— 同一キャンペーンの一環
いずれも「メンテナーアカウントの乗っ取り → 公式チャネルからの悪意あるバージョン公開」という共通パターンで、npm install や pip install を実行するだけで、15秒で開発マシンが完全に侵害されるものだった。
本記事では、インシデントの全容、影響確認方法、対応手順、予防策、AI Agent 環境の強化、継続運用までを網羅的にまとめる。
テンプレートファイル(.npmrc, pip.conf, CLAUDE.md, AGENTS.md) はすべて GitHub で公開している。
👉 TAKT-R-D/npm-pypi-supply-chain-hardening
目次
| Part | 内容 |
|---|---|
| Part 1 | axios(npm)侵害の詳細 |
| Part 2 | TeamPCP キャンペーン(LiteLLM, Telnyx, Trivy, KICS) |
| Part 3 | 侵害が確認された場合の対応 |
| Part 4 | 予防策(npm / PyPI / Composer / CI/CD) |
| Part 5 | 自分のマシンを棚卸しする |
| Part 6 | AI Agent 環境の強化 |
| Part 7 | 監視・継続運用 |
Part 1: axios npm 侵害
何が起きたか
2026年3月31日(UTC 00:21〜03:15)、axios の npm メンテナーアカウントが乗っ取られ、悪意のあるバージョンが公式パッケージとして公開された。
攻撃者はメンテナーの長期有効な npm アクセストークンを窃取し、GitHub Actions CI/CD を完全にバイパスして直接 npm に公開。正規リリースに含まれるはずの OIDC provenance / SLSA 署名は一切なかった。
影響を受けるバージョン
| パッケージ | 悪意のあるバージョン | 安全なバージョン |
|---|---|---|
| axios | 1.14.1, 0.30.4 | 1.14.0, 0.30.3 |
| plain-crypto-js | 4.2.1 | — (存在自体が不正) |
悪意のあるバージョンは約2〜3時間で npm から削除済み。現在 npm install axios を実行しても安全なバージョンがインストールされる。
攻撃の流れ
PHP プロジェクトへの注意
メインが PHP でも、フロントエンドツール(webpack, Vite, Laravel Mix 等)で npm 依存を持っているケースは多い。Huntress の調査では WordPress モジュールの node_modules 深部にまで plain-crypto-js が入り込んでいた事例が確認されている。PHP プロジェクトでも npm 依存の確認を省略しないこと。
影響確認手順
lockfile の確認
# package-lock.json
grep -r "axios.*1\.14\.1\|axios.*0\.30\.4\|plain-crypto-js" package-lock.json
# yarn.lock
grep -r "axios.*1\.14\.1\|axios.*0\.30\.4\|plain-crypto-js" yarn.lock
# 全プロジェクトを横断検索(Mac / Linux)
find ~ -name "package-lock.json" -exec grep -l "plain-crypto-js" {} \;
# Windows(PowerShell)
Get-ChildItem -Path $HOME -Recurse -Filter "package-lock.json" -ErrorAction SilentlyContinue |
Select-String -Pattern "plain-crypto-js" | Select-Object -Unique Path
node_modules の確認
# Mac / Linux
find ~ -path "*/node_modules/plain-crypto-js" -type d 2>/dev/null
マルウェアは自身の package.json を clean 版に差し替えるため、フォルダの中身が正常に見えることがある。フォルダの存在自体が侵害の証拠。
OS別 RAT アーティファクトの確認
| 確認項目 | Mac | Windows | Linux |
|---|---|---|---|
| RAT パス | /Library/Caches/com.apple.act.mond |
%PROGRAMDATA%\wt.exe |
/tmp/ld.py |
| 永続化 | なし(実行中のみ) | レジストリ Run キー MicrosoftUpdate
|
なし(再起動で消える) |
| プロセスチェーン | osascript → curl → com.apple.act.mond |
node → cmd/cscript → wt.exe |
python3 /tmp/ld.py |
macOS:
ls -la /Library/Caches/com.apple.act.mond
ps aux | grep -i "com.apple.act.mond"
Windows:
Test-Path "$env:PROGRAMDATA\wt.exe"
Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name "MicrosoftUpdate" -ErrorAction SilentlyContinue
Linux:
ls -la /tmp/ld.py
ps aux | grep "python3 /tmp/ld.py"
ネットワーク IOC
| 種別 | 値 |
|---|---|
| C2 ドメイン | sfrclak.com |
| C2 IP | 142.11.206.73 |
| ポート | 8000 |
Part 2: TeamPCP キャンペーン(PyPI + GitHub Actions)
概要
2026年3月19〜27日、TeamPCP と呼ばれる脅威グループが連鎖的なサプライチェーン攻撃を実行した。各侵害で得たクレデンシャルが次の攻撃を可能にするドミノ式の手法が特徴。
なお、axios の攻撃は TeamPCP とは別のアクターで、Google Threat Intelligence Group は北朝鮮の脅威アクター UNC1069 によるものと分析している。
タイムライン
| 日付 | 対象 | エコシステム | 悪意のあるバージョン |
|---|---|---|---|
| 3/19 | Trivy | GitHub Actions, Docker | v0.69.4、76タグ書き換え |
| 3/23 | Checkmarx KICS | GitHub Actions, OpenVSX |
kics-github-action 等 |
| 3/24 | LiteLLM | PyPI | 1.82.7, 1.82.8 |
| 3/27 | Telnyx | PyPI | 4.87.1, 4.87.2 |
| 3/31 | axios | npm | 1.14.1, 0.30.4 |
連鎖の仕組み
- Trivy → CI/CD シークレットと GitHub トークンを窃取
- 窃取した Trivy のクレデンシャル → Checkmarx KICS の GitHub Actions を侵害
- 窃取した CI/CD クレデンシャル → LiteLLM の PyPI 公開トークンを奪取
- 同じ手法 → Telnyx の Python SDK に適用
Python プロジェクトの確認
LiteLLM(v1.82.7, v1.82.8):
pip show litellm 2>/dev/null | grep -iE "name|version"
find / -name "litellm_init.pth" 2>/dev/null # v1.82.8 の悪意ある .pth ファイル
v1.82.8 の .pth ファイルは Python インタプリタの起動時に自動実行される。LiteLLM を明示的にインポートしなくてもマルウェアが動作する。
Telnyx(v4.87.1, v4.87.2):
pip show telnyx 2>/dev/null | grep -iE "name|version"
| パッケージ | C2 |
|---|---|
| LiteLLM |
models.litellm.cloud(正規ドメインのタイポスクワット) |
| Telnyx | 83.142.209.203 |
GitHub Actions の確認
# 影響を受けるアクションの検索
grep -r "aquasecurity/trivy-action\|aquasecurity/setup-trivy\|Checkmarx/kics-github-action\|Checkmarx/ast-github-action" .github/workflows/
# 脆弱 — タグでピン(攻撃者がタグを上書きした)
- uses: aquasecurity/trivy-action@v1
# 安全 — コミット SHA でピン(不変)
- uses: aquasecurity/trivy-action@a7a829a0e31574e4e2e4af6e8ab3bf4c0a32c371
Part 3: 侵害が確認された場合の対応
いずれかの IOC が検出された場合、そのマシンは完全に侵害されたものとして扱う。
- 即座にネットワークから隔離する
- マシンを再イメージ(クリーンインストール)する — インプレースの修復は不可
-
すべてのクレデンシャルをローテーションする:
- npm / PyPI / Composer トークン
- AWS / GCP / Azure クレデンシャル
- SSH 秘密鍵
- GitHub / GitLab トークン
- CI/CD シークレット
-
.envファイルに含まれるすべての値 - データベースパスワード
- すべての API キー(LLM プロバイダーキーを含む)
- CI/CD パイプラインを確認: 攻撃期間中にビルドが走った場合、そのシークレットもローテーション
- クラウド環境の監査ログを確認
Part 4: 予防策
npm(JavaScript / TypeScript)
バージョンの完全固定
{
"dependencies": {
"axios": "1.14.0" // ← "^1.14.0" ではなく exact
}
}
npm config set save-exact true
lockfile の厳格な運用
# CI/CD では npm install ではなく npm ci を使う
npm ci
package-lock.json は必ず Git にコミットする。
lifecycle scripts の無効化
npm config set ignore-scripts true
# .npmrc
ignore-scripts=true
ignore-scripts=true は lifecycle scripts をブロックするが、npm install 時のコード実行パスはこれだけではない(例: Git 実行パス)。ignore-scripts 単体に頼らず、バージョン固定、lockfile 強制、pnpm 採用と組み合わせること。
pnpm の採用
pnpm(v10+)はビルドスクリプト承認モデルを提供する。依存パッケージの lifecycle scripts はデフォルトでブロックされ、onlyBuiltDependencies 設定や pnpm approve-builds コマンドで明示的に承認する必要がある。
{
"pnpm": {
"onlyBuiltDependencies": ["sharp", "esbuild", "prisma"]
}
}
モノレポでは
pnpm-workspace.yamlにも設定可能。v10 未満では動作が異なる場合があるためpnpm --versionで確認。
新規パッケージのクールダウン
# 公開から72時間以内のパッケージを拒否(npm v11+)
# キー名は npm バージョンで異なる可能性あり。npm help config で確認。
npm config set minimumReleaseAge 3d
provenance の確認
npm audit signatures
PyPI(Python)
ハッシュ検証 + wheel 限定
# ハッシュ検証 + ソースビルド禁止(setup.py 実行パスを完全排除)
pip install --require-hashes --only-binary :all: -r requirements.txt
--require-hashes と --only-binary :all: の組み合わせが pip が提供する最も強力なインストール時防御。
依存関係のロック
# pip-tools
pip-compile requirements.in
pip-sync requirements.txt
Composer(PHP)
# lockfile を尊重し、install scripts を無効化
composer install --no-dev --no-scripts
# 脆弱性チェック
composer audit
PHP プロジェクト内の npm 依存も忘れずに確認:
find /path/to/php-projects -name "package-lock.json" -exec grep -l "plain-crypto-js" {} \;
CI/CD(GitHub Actions)
コミット SHA でピン
# 脆弱
- uses: actions/checkout@v4
# 安全
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
Environment Reviewers
本番シークレットにアクセスするワークフローには required reviewers を設定。
self-hosted runner の回避
パブリックリポジトリでの self-hosted runner は強く非推奨。GitHub-hosted または JIT ランナーを使用。
CODEOWNERS
# .github/CODEOWNERS
/.github/workflows/ @security-team @devops-lead
Dependabot + Dependency Review
- uses: actions/dependency-review-action@<sha>
with:
fail-on-severity: high
Dependabot の自動マージは絶対に使わない。 PR のオープンのみに設定し、手動レビュー必須。
予防策チェックリスト
| 対策 | npm | PyPI | Composer | CI/CD |
|---|---|---|---|---|
| exact version 固定 | save-exact true |
== で指定 |
^/~ 除去 |
— |
| lockfile コミット | package-lock.json |
poetry.lock 等 |
composer.lock |
— |
| lockfile only install | npm ci |
pip-sync |
composer install |
全て |
| install scripts 無効化 | ignore-scripts=true |
--only-binary :all: |
--no-scripts |
全て |
| ハッシュ要求 | — | --require-hashes |
— | CI で強制 |
| provenance 確認 | npm audit signatures |
SLSA / Sigstore | — | provenance 要求 |
| lockfile PR レビュー | 必須 | 必須 | 必須 | ブランチ保護 |
| SBOM 生成 |
syft / cdxgen
|
同左 | 同左 | ビルドごと |
| CODEOWNERS | — | — | — | .github/CODEOWNERS |
| Environment reviewers | — | — | — | publish/deploy に必須 |
Part 5: 自分のマシンを棚卸しする
影響を受けたすべてのツールが公式アナウンスを出すわけではない。例えば Anthropic は Claude Code の npm 版を非推奨としてネイティブインストーラーに移行したが、axios を transitive dependency として含む多くのツールはそのようなアナウンスを出さない可能性が高い。上流からの通知を待たず、自分のマシンを能動的に確認。
クイックスキャン(コピペで実行、2分)
# ===== npm: グローバルインストールの確認 =====
echo "=== npm global: axios ==="
npm list -g axios 2>/dev/null
echo "=== npm global: plain-crypto-js(存在してはならない) ==="
npm list -g plain-crypto-js 2>/dev/null
# ===== PyPI: 侵害パッケージの確認 =====
echo "=== Python: litellm ==="
pip show litellm 2>/dev/null | grep -iE "name|version"
echo "=== Python: telnyx ==="
pip show telnyx 2>/dev/null | grep -iE "name|version"
# ===== ファイルシステム =====
echo "=== plain-crypto-js(ホーム以下) ==="
find ~ -path "*/node_modules/plain-crypto-js" -type d 2>/dev/null
echo "=== 悪意のある axios バージョン ==="
find ~ -path "*/node_modules/axios/package.json" \
-exec grep -l '"version": "1.14.1"\|"version": "0.30.4"' {} \; 2>/dev/null
echo "=== litellm_init.pth(存在してはならない) ==="
find ~ -name "litellm_init.pth" 2>/dev/null
Windows(PowerShell)版
Write-Host "=== npm global: axios ==="
npm list -g axios 2>$null
Write-Host "=== npm global: plain-crypto-js ==="
npm list -g plain-crypto-js 2>$null
Write-Host "=== Python: litellm ==="
pip show litellm 2>$null | Select-String "Name|Version"
Write-Host "=== Python: telnyx ==="
pip show telnyx 2>$null | Select-String "Name|Version"
Write-Host "=== plain-crypto-js ==="
Get-ChildItem -Path $HOME -Recurse -Directory -Filter "plain-crypto-js" -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -match "node_modules" }
Write-Host "=== litellm_init.pth ==="
Get-ChildItem -Path $HOME -Recurse -Filter "litellm_init.pth" -ErrorAction SilentlyContinue
結果の見方
| 検出内容 | 対応 |
|---|---|
plain-crypto-js がどこかに存在 |
侵害確定。 Part 3 を即実行 |
| axios 1.14.1 / 0.30.4 が存在 | 侵害確定。 Part 3 を即実行 |
litellm_init.pth が存在 |
侵害確定。 Part 3 を即実行 |
| litellm 1.82.7 / 1.82.8 | 侵害確定。 Part 3 を即実行 |
| telnyx 4.87.1 / 4.87.2 | 侵害確定。 Part 3 を即実行 |
| axios(安全版)がグローバルツール内に存在 | ツールの要否を見直し。npm 非依存の代替を検討 |
| 何も検出されない | 問題なし。定期的に再実行 |
Part 6: AI Agent 環境の強化
なぜ AI Agent に特別な注意が必要か
AI コーディングエージェント(Claude Code, Codex, Antigravity 等)は npm install や pip install を日常的に実行する。人間なら見慣れないパッケージに気づいて止まるかもしれないが、Agent は躊躇なく実行する。
Agent 指示ファイル対応表
| Agent | 指示ファイル | スコープ |
|---|---|---|
| Claude Code | CLAUDE.md |
プロジェクトルート → CWD。~/.claude/CLAUDE.md でグローバル設定も可 |
| Codex(OpenAI) | AGENTS.md |
~/.codex/ → プロジェクトルート → CWD。階層マージ |
| Antigravity(Google) |
AGENTS.md / GEMINI.md
|
v1.20.3+ で AGENTS.md サポート。GEMINI.md 優先 |
| Cursor | .cursorrules |
プロジェクトルート。AGENTS.md も読む |
AGENTS.md がクロスツール標準になりつつある。 CLAUDE.md + AGENTS.md の両方に同一のルールを記載するのが最も確実。
👉 テンプレート: templates/CLAUDE.md / templates/AGENTS.md
多層防御の構成
| レイヤー | 機能 | 防御対象 |
|---|---|---|
| Agent 指示(CLAUDE.md / AGENTS.md) | pnpm 使用、ハッシュ要求、人間承認を指示 | Agent が誤ったツール選択 / 自律的にインストール |
.npmrc ignore-scripts=true
|
lifecycle scripts をブロック | Agent が指示を無視して npm を実行 |
pip.conf require-hashes + only-binary
|
未検証 pip install をブロック | 未検証 Python パッケージのインストール |
Corepack packageManager
|
pnpm 利用を促進(完全ブロックではない — CI で補完) | Agent が npm にデフォルト |
pnpm onlyBuiltDependencies(v10+) |
承認済みパッケージのみ scripts 許可 | 未承認パッケージの postinstall |
| Human-in-the-Loop | 人間が lockfile 変更前に承認 | ゼロデイ攻撃 |
Corepack の注意点: npm はデフォルトで Corepack の shim 対象ではない。packageManager: "pnpm@..." は強い運用上のシグナルだが、npm 実行の完全遮断策ではない。CI で package-lock.json の存在を検出してエラーにするなど、追加のチェックで補完。
👉 設定ファイル: templates/.npmrc / templates/pip.conf
Part 7: 監視・継続運用
予防策は一度設定して終わりではない。
lockfile 変更には PR レビューを必須にする
package-lock.json, pnpm-lock.yaml, requirements.txt, composer.lock のすべての変更は PR でレビュー。ブランチ保護ルールで少なくとも1名の承認を要求。
新規依存の承認プロセス
Dependabot / Renovate の自動マージは禁止。PR のオープンのみに設定し、以下を手動レビュー:
- 新しい transitive dependency の出現
- 複数パッチをスキップするバージョンジャンプ
- lifecycle scripts を突然追加した依存
SBOM の生成
syft dir:. -o spdx-json > sbom.json
ビルドごとに生成し、ビルドアーティファクトと一緒に保存。
Provenance / Attestation の定期検証
npm audit signatures
gh attestation verify <artifact> --owner <org>
以前 provenance を持っていたパッケージが突然なくなった場合は高優先度アラート。
定期監査
npm audit --audit-level=high
pip-audit
composer audit
参考情報
axios:
TeamPCP:
- Kaspersky / Datadog / Unit 42 / ReversingLabs / Arctic Wolf
テンプレート:
- 👉 TAKT-R-D/npm-pypi-supply-chain-hardening — CLAUDE.md, AGENTS.md, .npmrc, pip.conf