npmサプライチェーン攻撃Shai-Hulud:署名検証もSBOMも無意味だった理由
2026年8月4日、npmの人気パッケージ群を管理するメンテナのGitHubアカウントが乗っ取られ、悪意あるバージョンが公開された。最終的に868パッケージ・1,381バージョンが感染し、月間インストール数の合計は20億を超えた。
そして、用意していたサプライチェーンセキュリティの防御が全部通過した。
攻撃の概要
keyv・flat-cache・file-entry-cacheなどのメンテナ Jared Wray のGitHubアカウントが乗っ取られ、preinstall フックを仕込んだ悪意あるバージョンが公開された。フックはBunランタイムをダウンロードし、難読化ペイロード Math_Symbol.js(728KB)を実行。約140パターンのファイルからnpmトークン・GitHubセッション・AWSキー・Kubernetesサービスアカウント・HashiCorp Vaultトークン・.claude/credentials.json 等のAI開発ツール認証情報を窃取した。
盗んだnpmトークンで他パッケージにも自己複製するワームとして動作し、活発なフェーズでは数分ごとに50〜100パッケージが新規感染した。
ESLintの依存チェーン(ESLint → file-entry-cache → flat-cache → keyv)により、ESLintを使うプロジェクトはほぼ全員が被害範囲内に入った。npm audit は何も検出しない。CVEは存在しない。
なぜすべての検証ツールが通過したか
攻撃者はレジストリへの不正アクセスではなく、メンテナのGitHubアカウントを乗っ取ってmainブランチに悪意あるコードをプッシュした。
その後はプロジェクト自身のCI/CDが動いた。GitHub Actionsが実行され、Sigstoreが署名し、SLSAプロベナンスが生成された。npmパッケージは登録済みの正規の鍵で署名された。

署名検証・プロベナンス証明・SBOM生成は、いずれも「正規のキーで署名され、正規のビルドパイプラインから生成されたか」を確認する。ソースコードに悪意があるかどうかは検証できない。
TOTP 2FAはリアルタイムフィッシングで突破された。アカウントを乗っ取られた瞬間、下流のすべてが「正規」になった。
インシデントレスポンスが見落としたパーシスタンス層
多くの対応ガイドはnpmのlifecycleフック除去で終わっている。しかし攻撃はIDEの設定ファイルにも書き込みを行った。
.vscode/tasks.jsonのrunOn: folderOpenタスク — プロジェクトをVS Codeで開くと自動実行.claude/settings.jsonのSessionStartフック — Claude Codeのセッション開始時に自動実行
これらはnpmとは独立して動作する。--ignore-scripts では防げない。調査のためにVS Codeを開くと、フックが再発火する。
さらに、GitHubトークンの失効を監視するデッドマンズスイッチが常駐していた。トークンをrevokeする前にこのスイッチを除去しないと、追加ペイロードが実行される。正しい対応順序は「IDEフック除去 → デッドマンズスイッチ除去 → トークンrevoke」だ。
実践的な対応手順(lockfileチェック・デッドマンズスイッチ除去コマンド・revoke順序)はQiitaの記事に詳しくまとめている。
サプライチェーンセキュリティへの示唆
Shai-HuludのC2(コマンド&コントロール)インフラには、従来のマルウェア対策が通用しない2つの設計が使われていた。
① C2設定をEthereumスマートコントラクトに格納
C2サーバのアドレスはEthereumスマートコントラクト(0xE1f2395ee43e45A1556EC6438a88c31B83493103)にAES-256-GCM暗号化して保存されており、75の公開RPCエンドポイント経由で取得する設計だった。従来のC2対策は「悪意あるドメインをシンクホール(別サーバに誘導して無効化)する」手法が主流だが、固定ドメインが存在しないオンチェーン参照にはシンクホールが効かない。ブロックリストに載せるべきIPアドレスもない。

② BunランタイムをGitHub公式ドメインからダウンロード
マルウェアの実行エンジンとなるBunバイナリは github.com/oven-sh/bun/releases/ から取得する。多くの企業ファイアウォールはGitHubを「信頼済みドメイン」として扱うため、このトラフィックはレピュテーションフィルタを素通りする。ダウンロードされるのは本物のBun公式リリース(正規署名付き)であり、マルウェアスキャナも安全と判定する。実行後はバイナリを削除するため、フォレンジック調査でも痕跡が残らない。
現行のサプライチェーンセキュリティモデルの構造的な限界が明らかになった。検証はIDと出所を確認するが、IDは侵害される。信頼の連鎖は最も弱いリンクで断ち切られる。
実効性のある対策の方向性:
- FIDO2/パスキー によるメンテナアカウント保護(TOTPはリアルタイムフィッシングで突破可能)
- 異常パブリッシュパターンの行動検知(これまでpreinstallスクリプトを持たなかったパッケージへの突然の追加)
- インシデントレスポンスにIDEの設定ファイル監査を含める(npmだけ見ていては不十分)
「--ignore-scripts が有効だった層」はnpmではなく、IDEにあった。
実践的な対応手順(lockfile確認・デッドマンズスイッチ除去・revoke順序)はQiitaへ。VS CodeとClaude CodeのIDEパーシスタンス詳細解説はDev.toへ。各リンクは下部を参照。
この記事は役に立ちましたか?