← ブログに戻る

npmサプライチェーン攻撃Shai-Hulud:署名検証もSBOMも無意味だった理由

2026年8月4日、npmの人気パッケージ群を管理するメンテナのGitHubアカウントが乗っ取られ、悪意あるバージョンが公開された。最終的に868パッケージ・1,381バージョンが感染し、月間インストール数の合計は20億を超えた。

そして、用意していたサプライチェーンセキュリティの防御が全部通過した。

攻撃の概要

keyvflat-cachefile-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パッケージは登録済みの正規の鍵で署名された。

なぜすべての検証ツールが通過したか — Shai-Hulud 攻撃の信頼チェーン

署名検証・プロベナンス証明・SBOM生成は、いずれも「正規のキーで署名され、正規のビルドパイプラインから生成されたか」を確認する。ソースコードに悪意があるかどうかは検証できない。

TOTP 2FAはリアルタイムフィッシングで突破された。アカウントを乗っ取られた瞬間、下流のすべてが「正規」になった。

インシデントレスポンスが見落としたパーシスタンス層

多くの対応ガイドはnpmのlifecycleフック除去で終わっている。しかし攻撃はIDEの設定ファイルにも書き込みを行った。

  • .vscode/tasks.jsonrunOn: folderOpen タスク — プロジェクトをVS Codeで開くと自動実行
  • .claude/settings.jsonSessionStart フック — 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アドレスもない。

C2 インフラの比較 — なぜシンクホールが効かないか

② 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へ。各リンクは下部を参照。