1386文字
7分
編集

Mini Shai-Hulud 2nd Waveの概要と対策

2026年5月11日から発生した「Mini Shai-Hulud」サプライチェーン攻撃の第二波では、@tanstackエコシステムを中心とする多数のnpmパッケージに悪意のあるバージョンが公開されました。

#侵害手法

攻撃者は以下の3段階で正規のリリースパイプラインを乗っ取りました。

  1. Pwn Request: pull_request_targetトリガーを悪用し、悪意のあるプルリクエストを作成。
  2. キャッシュポイズニング: 悪意のあるバイナリをGitHub Actionsのキャッシュに混入。
  3. OIDCトークンの抽出: 正規のリリース処理中にランナーのメモリからOIDCトークンを抽出し、npmへマルウェア入りパッケージを公開。
  • 監視の強いNode.jsを逃れるため、不正コードの実行にはBunランタイムが使用された。
  • 正規のOIDCトークンが奪取されたため、npmのProvenanceは正常と判定される。

#感染した場合の被害

被害は主に以下の3点に分類されます。

  • 広範な認証情報の窃取: パッケージのインストール時にマルウェアが実行され、ローカル環境だけでなく、クラウドやCI/CD環境へのAPIアクセスも行い認証情報を盗み出します。

    • GitHubトークン、npmパブリッシュトークン
    • AWS、GCP、Azureのクラウド認証情報
    • Kubernetesサービスアカウント、HashiCorp Vaultトークン
    • .envファイル内の環境変数
    • SSHキー(~/.ssh/id_rsaなど)
    • 1PasswordやBitwardenなどのパスワードマネージャーのデータ
  • マルウェアの自己増殖: 盗み出したnpmトークンを利用して、被害者が公開権限を持つ別の無関係なパッケージにもマルウェアを仕込み、自動的に新しいバージョンを公開します。

  • ホームディレクトリの破壊(デッドマンズ・スイッチ): マルウェアは被害者のシステムに監視デーモンを常駐させます。情報流出に気づいた開発者がGitHubトークンを無効化すると、それを検知して被害者のホームディレクトリを全削除しようと試みます。

#感染有無の確認と対処手順

トークンを先に無効化するとホームディレクトリが破壊されるリスクがあるため、必ず以下の順序で確認と対処を行ってください。

  1. 永続化デーモンの確認と削除: トークンを無効化する前に必ず実施してください。 マルウェアが常駐していないか確認します。以下のファイルが存在する場合は感染しています。直ちにプロセスを停止し、ファイルを削除してください。

    • macOS: ~/Library/LaunchAgents/com.user.gh-token-monitor.plist
    • Linux: ~/.config/systemd/user/gh-token-monitor.service
  2. パッケージとロックファイルの監査: package-lock.jsonyarn.lockpnpm-lock.yamlなどに、悪意のあるパッケージが含まれていないか検索します。

    • @tanstack/*react-routerhistoryなど)の2026年5月11日以降に不自然にバージョンアップされたもの。
    • パッケージ内に @tanstack/setup という不審な依存関係が含まれていないか確認します。
  3. 不審なファイルやログの確認: CIのビルドログやローカル環境のファイルシステムで、以下の痕跡がないか確認します。

    • ファイル: router_init.jspgmonitor.py が生成されていないか。
    • ログ: パッケージのインストール中に、意図せず bun ランタイムがダウンロード・実行されている形跡(bun run tanstack_runner.js など)がないか。
  4. 漏洩した認証情報のローテーション: ステップ1のデーモン削除が確実に完了していることを確認した上で、漏洩の可能性があるすべての認証情報(GitHub、npm、クラウドプロバイダ、SSHキーなど)を無効化し、再発行してください。

#自衛手段

npmパッケージを利用する側としては、以下の対策が有効です。

  1. Lifecycle scriptの無効化: npm ci --ignore-scripts を標準とし、パッケージインストール時の不正コード実行(preinstallprepareフックなど)を防ぎます。
  2. Dependency Cooldownの設定(min-release-age): npm v11以降では .npmrcmin-release-age=7(推奨7日、最低3日)を設定し、公開直後のパッケージのインストールを抑止します。攻撃パッケージは数日以内にテイクダウンされるケースが多いため、時間差による検疫が有効です。
  3. ロックファイルの整合性検証: package-lock.jsonpnpm-lock.yamlintegrity フィールドによるハッシュ検証を必須とし、CIで不一致があった場合はブロックします。npm ciyarn install --frozen-lockfilepnpm install --frozen-lockfileを使用することで、整合性を検証できます。

パッケージ開発者としては、以下の対策が有効です。

  1. OIDCトークンのスコープ最小化: GitHub Actionsのワークフローにおいて、デフォルトで permissions: id-token: none を設定し、npm publishを実際に行うJobのみに id-token: write を付与します。
  2. pull_request_targetを避ける: pull request自体は信頼できるが、そのPRがマージされた際のCI/CD環境のワークフローは、信頼できないPRに書き換えられる可能性があるため、PR内部のコードを実行するワークフロー(pull_request_target)での公開操作は避けるべきです。

#モダンなパッケージマネージャによる防御

最新のpnpm(v11以降)やBunを使用していれば、デフォルトの設定だけで今回の攻撃の大部分を防ぐことが可能です。

  • pnpm v11: セキュリティ重視のデフォルト設定が導入されています。
    • blockExoticSubdeps=true: 今回の攻撃で使われたような、不正なGitHubリポジトリを直接参照する依存関係(github:tanstack/router#...)の解決をデフォルトでブロックします。
    • strictDepBuilds=true: ユーザーが明示的に許可(allowBuilds)していないパッケージのLifecycle scriptの実行をブロックします。
    • minimumReleaseAge: 新規公開されたパッケージに対してデフォルトで24時間のインストール待機期間が設けられ、即時の感染を防ぎます。
  • Bun: デフォルトで強力なLifecycle scriptの制限を持っています。
    • trustedDependencies: 主要なパッケージのホワイトリストに含まれるものか、ユーザーが明示的に信頼したパッケージ以外は prepare などのスクリプトが一切実行されないため、初期侵入を遮断できます。

#参考文献