ミニ・シャイ=フルード・ワーム:TanStack NPM サプライチェーン攻撃の分析

JavaScript エコシステムは長らくサプライチェーン攻撃の標的となってきましたが、最近の TanStack パッケージに関連する事案は、これらの脅威における恐ろしい進化を浮き彫りにしました。正当なパッケージの侵害から始まったものが、「ミニ・シャイ=フルード(Mini Shai-Hulud)」ワームへと進化しました。これは、CI/CD パイプラインを乗っ取り、開発者の秘密情報を盗み、資格情報を失効させようとする者に報復を与えるように設計された、自己増殖型のマルウェアです。

この攻撃は、単なるパスワードの盗難ではありません。現代のパッケージマネージャーや CI/CD ワークフローに内在する信頼モデルを巧妙に悪用したものです。TanStack の事案と、その後のコミュニティでの議論を分析することで、オープンソースソフトウェアの公開と利用における重大な脆弱性を明らかにすることができます。

攻撃の解剖学

「ミニ・シャイ=フルード」ワームは、開発ライフサイクルの核心を標的にします。報告書や TanStack のポストモーテム(事後分析)によると、攻撃者は CI/CD パイプラインの侵害に成功しました。このワームは、これらのパイプラインを乗っ取って開発者の秘密情報を盗み、その後、盗んだ資格情報を使用して正当なパッケージの悪意のあるバージョンを公開します。

攻撃ベクトル:CI/CD と OIDC トークン

この事案から得られた最も重要な知見の一つは、「Trusted Publishing」の失敗です。多くの開発者は、GitHub secrets に長期間有効な NPM トークンを保存することを避けるため、Trusted Publishing (OIDC トークン) に移行してきました。しかし、コミュニティメンバーが指摘するように、これにより、通常ローカルの npm publish を制限する第2要素認証 (2FA) が失われます。

もし攻撃者が CI パイプラインにアクセスしたり、リポジトリの管理者トークンを盗んだりした場合、公開イベントをトリガーできます。OIDC トークンは GitHub Actions ランナーによって自動的に生成されるため、攻撃者は物理的な 2FA デバイスを必要とせずに悪意のあるコードを公開できます。これにより、信頼が完全に GitHub 環境内に置かれるという盲点が生じ、CI パイプラインが高価値の標的となります。

「報復的」なペイロード

このワームの最も衝撃的な側面は、そのペイロードです。トークンの盗難を超えて、このマルウェアは感染したマシンに「デッドマンズ・スイッチ(死のスイッチ)」をインストールします。

トークンの失効時には注意してください。ペイロードは、~/.local/bin/gh-token-monitor.sh に、systemd ユーザーサービス (Linux) または LaunchAgent com.user.gh-token-monitor (macOS) としてデッドマンズ・スイッチをインストールするようです。これは盗んだトークンを使用して 60 秒ごとに api.github.com/user をポーリングし、もしトークンが失効(HTTP 40x)した場合、rm -rf ~/ を実行します。

このレベルの報復性は、サプライチェーン攻撃においては稀であり、侵害されたトークンを失効させることで状況を改善しようとする開発者にとって、セキュリティ侵害をデータ破棄の潜在的な大惨事へと変貌させます。

エコシステムの脆弱性

TanStack の侵害は、NPM エコシステムの根本的なアーキテクチャに関する激しい議論を再燃させました。

ライフサイクル・スクリプトの危険性

このワームは、prepare フックを利用してペイロードを実行しました。これは、繰り返されるセキュリティ上の欠陥を浮き彫りにしています。すなわち、ライフサイクル・スクリプト(preinstallpostinstallprepare など)のデフォルト実行です。

批判的な意見では、2026 年にこれらのスクリプトをデフォルトで有効にすることは「明らかな職務怠慢」であると主張されています。これらのスクリプトはインストール中に自動的に実行されるため、単一の侵害された依存関係、あるいは依存関係の依存関係であっても、ユーザーの明示的な操作なしに開発者のマシンやビルドサーバー上で任意のコードを実行できてしまいます。

「アンパブリッシュ不可」ポリシー

TanStack のポストモーテムは、復旧プロセスにおける苛立たしい障害を明らかにしました。NPM のポリシーでは、他のパッケージが依存している場合、パッケージのアンパブリッシュ(公開停止)は禁止されています。これは、侵害が検出された後でも、悪意のある tarball がサーバー側で NPM セキュリティチームが手動で削除するまで数時間、利用可能であり続け、新規インストールを行うユーザーに対して脆弱性の窓が開いたままになることを意味します。

プロジェクトを保護する方法

コミュニティは、将来の同様の攻撃のリスクを軽減するために、いくつかの具体的な戦略を提案しています。

1. 依存関係のクールダウン期間を設ける

「ゼロデイ」の悪意のあるリリースを避ける最も効果的な的な方法は、最小リリース年齢を設定することです。新しいパッケージのバージョンを数日間遅らせてインストールすることで、コミュニティやセキュリティスキャナーが、本番環境に導入される前に悪意のあるコードを検出して報告する時間を稼ぐことができます。

パッケージマネージャーによって設定が異なります:

  • NPM: .npmrc $\rightarrow$ min-release-age=7 (days)
  • pnpm: pnpm-workspace.yaml $\rightarrow$ minimumReleaseAge: 10080 (minutes)
  • Yarn: .yarnrc.yml $\rightarrow$ npmMinimalAgeGate: 7d
  • Bun: bunfig.toml $\rightarrow$ minimumReleaseAge = 604800 (seconds)

2. ライフサイクル・スクリプトを無効にする

インストール中の悪意のあるコードの自動実行を防ぐため、スクリプトをグローバルに無効にします。.npmrc に以下を追加してください: ignore-scripts=true

3. CI/CD パイプラインを強化する

OIDC トークンの乗っ取りを防ぐために、以下のアーキテクチャ上の変更を検討してください:

  • Isolation (分離): リリース・パイプラインを、メインの開発プロジェクトとは完全に分離された環境で実行します。
  • Staged Publishing (段階的公開): 人間が 2FA を使用して、ビルドされたアーティファクトをステージング領域から最終的なレジストリへ昇格させるシステムを構築し、CI パイプラインの侵害単独ではパッケージの公開に十分ではないようにします。
  • Strict Pinning (厳格な固定): package.json からキャレット (^) やチルダ (~) を記号を削除し、正確なバージョンを固定することで、侵害されたパッチ・リリースを自動的に取得してしまうことを防ぎます。

結論

ミニ・シャイ=フルード・ワームは、警鐘を鳴らしています。これは、現在の JavaScript エコシステムの信頼モデル(膨大な数の推移的依存関係と自動公開に依存している状態)が、本質的に脆弱であることを示しています。SLSA provenance attestation のようなツールは一歩前進ですが、もしアテステーションを生成するシステム自体が侵害されていた場合、それらは不十分です。真のセキュリティを実現するには、明示的な信頼への移行、自動スクリプトへの依存の低減、そして依存関係のアップデートに対するより慎重なアプローチが必要となります。

Sources