【サプライチェーン攻撃】2026年5月11日 TanStack 乗っ取り事件 - SLSA provenance が効かなかった日

【サプライチェーン攻撃】2026年5月11日 TanStack 乗っ取り事件 - SLSA provenance が効かなかった日

作成日:
更新日:

事件の概要(早見表)

2026年5月11日、TanStackのnpmパッケージ群に悪意あるバージョンが公開されました。

TanStack公式ポストモーテムによると、攻撃者はnpmトークンを盗んで直接公開したのではありません。pull_request_target、GitHubActionsキャッシュ汚染、GitHubActionsランナー内のOIDCトークン抽出を連鎖させ、正規のリリースワークフローの身分でnpmへ公開しました。

項目内容
発生日2026年5月11日
悪意ある公開時刻19:20〜19:26UTC
影響範囲42個の@tanstack/*パッケージ、各2バージョン、合計84バージョン
初期公開元TanStack/routerのリリースワークフロー
初期侵入口フォークPRを使ったpull_request_targetワークフロー悪用
主要手口GitHubActionsキャッシュ汚染、OIDCトークンのランナーメモリ抽出
ペイロードrouter_init.js、約2.3MBの難読化JavaScript
主要な痕跡optionalDependencies@tanstack/setuprouter_init.js、Session/Oxen系通信
検知StepSecurity所属の外部研究者が公開から約20〜26分で報告
公開後対応初回公開から約1時間43分で84バージョンを非推奨化、npm側でtarball削除
関連アドバイザリGHSA-g7cv-rxg3-hmpx、Snyk記事ではCVE-2026-45321として記載

注意したいのは、TanStack公式が「現在公開されている全TanStackパッケージは安全」と明記している点です。この記事は「いまTanStackを使ってはいけない」という話ではありません。むしろ、正規のCI/CDから正規のOIDC身分で悪意あるパッケージが出てしまった、という信頼モデルの破れ方を整理する記事です。

なぜ「いつもの攻撃」と違うのか

axios事件(メンテナーアカウント侵害)との比較

2026年3月31日のaxios事件では、Huntressの調査によると、攻撃者がnpmメンテナーアカウントjasonsaaymanを侵害し、axios@1.14.1axios@0.30.4を手動公開しました。悪意ある依存plain-crypto-js@4.2.1を追加し、そのpostinstallでWindows/macOS/Linux向けRATを落とす構造でした。

この攻撃は「メンテナーアカウントの侵害」でした。正規のGitHubActionsリリース経路を迂回し、npm CLIと長期トークンで公開しています。過去記事でも、axios事件は「npmアカウントと長期トークンが破られると、人気パッケージでも一瞬で汚染される」事例として整理しました。

一方、TanStack事件は違います。攻撃者はnpmトークンを盗んでいません。GitHubActionsの正規ランナー上でコード実行を得て、そのランナーに付与されていたOIDC権限を使い、npmのtrusted publishing経路に見える形で公開しました。

つまり、axios事件は「公開者の身分が偽装された事件」、TanStack事件は「公開者の身分は正規だが、公開プロセスの内部状態が汚染された事件」と見るとわかりやすいです。

SLSA provenance有効状態で悪意あるパッケージが公開された史上初の意味

SnykとUnit42は、この事件を「有効なSLSA provenanceを持つ悪意あるnpmパッケージが確認された初の公開事例」と表現しています。ここでの「史上初」は、公開された調査記事で確認できる範囲の「first documented case」という意味で扱います。

npm provenanceは、パッケージがどのソースとビルド環境から公開されたかを検証可能にする仕組みです。npm公式ドキュメントも、provenanceは「ソースコードとビルド手順への検証可能なリンク」を提供するが、「悪意あるコードが含まれないこと」は保証しないと説明しています。

TanStack事件では、Sigstoreは間違っていません。npmのprovenance検証も、意味を失ったわけではありません。問題は「正規ワークフローの中で、すでに攻撃者コードが走っていた」ことです。

これは、荷物に貼られた配送伝票が本物でも、倉庫の中で荷物がすり替えられていたら検品にならない、というタイプの失敗です。

攻撃のタイムラインと侵害チェーン

TanStack公式ポストモーテムの時系列を、攻撃チェーンとして整理します。

時刻(UTC)出来事
2026-05-10 17:16攻撃者がTanStack/routerのフォークzblgg/configurationを作成
2026-05-10 23:29悪意あるコミット65bf499d...を作成。偽装名義はclaude <claude@users.noreply.github.com>
2026-05-11 10:49PR#7378「WIP:simplify history build」を作成
2026-05-11 11:11pull_request_targetワークフローがフォーク由来コードを実行
2026-05-11 11:29汚染されたpnpmストアがGitHubActionsキャッシュに保存される
2026-05-11 11:31攻撃者がPRを0ファイル差分に戻し、PRを閉じてブランチ削除
2026-05-11 19:15正規のリリースワークフローが再実行され、汚染キャッシュを復元
2026-05-11 19:20@tanstack/history@1.161.9など42パッケージの一部がnpmに公開
2026-05-11 19:262回目のリリースランで残りの悪意あるバージョンが公開
2026-05-11 19:46StepSecurity所属の研究者ashishkurmiがIssue#7383で報告
2026-05-11 20:19最初の2バージョンが非推奨化
2026-05-11 21:03全84バージョンの非推奨化が完了
2026-05-11 22:13〜23:55npmセキュリティチームがtarballをレジストリ側で削除

攻撃のいやらしさは、11:31時点でPRの見た目がほぼ無害化されていたことです。表面上は閉じられたPRですが、汚染されたキャッシュだけが残り、約8時間後に正規リリースで復元されました。

攻撃の技術的解剖

GitHubActionsの3脆弱性連鎖

TanStack公式は、今回の根本原因を3つの要素の連鎖として整理しています。どれか1つだけでは成立しにくく、3つがつながったことでnpm公開まで到達しました。

1つ目はpull_request_targetの危険な使い方です。pull_request_targetはベースリポジトリの文脈で実行されるため、ラベル付けやコメントのような「信頼済み操作」には便利です。しかし、そこでフォークPRのコードをチェックアウトしてビルドすると、フォーク由来コードがベースリポジトリ側の権限境界に入り込みます。

2つ目はGitHubActionsキャッシュ汚染です。actions/cacheのpost-job保存は、ワークフローのGITHUB_TOKEN権限だけでは説明できないランナー内部の仕組みで動きます。permissions:contents:readにしても、キャッシュ書き込みまで止まるとは限りません。攻撃者は正規リリースが後で読むキャッシュキーを事前計算し、そこに汚染されたpnpmストアを置きました。

3つ目はOIDCトークンのメモリ抽出です。release.ymlにはnpm trusted publishingに必要なid-token:writeがありました。汚染キャッシュから実行された攻撃者コードは、Linux上の/proc/<pid>/memを読み、GitHubActionsのRunner.Workerプロセス内にあるOIDCトークンを取り出して、registry.npmjs.orgへ直接POSTしました。

OIDCトークンが正規ランナー内で取得される構造の悪用

OIDC自体は、長期npmトークンを減らすための重要な仕組みです。問題は、OIDCトークンが「正規のランナー内で、正規のワークフロー権限として取得可能だった」ことです。

TanStack事件では、npmのPublishPackagesステップはテスト失敗により実行されていません。それでもnpmには、正規ワークフローのOIDC身分で悪意あるパッケージが届きました。攻撃者がワークフロー内の公開ステップを使ったのではなく、ランナー上で横からOIDCトークンを盗み、npmへ直接投げたためです。

ここが従来の「npm token leaked」と根本的に違います。長期トークンを消しても、ランナー内で任意コード実行を許せば、短命トークンもその場で盗まれます。

ペイロード動作(認証情報窃取→自己複製→他リポジトリ拡散)

悪意あるTanStack tarballには、router_init.jsという約2.3MBの難読化ファイルが入りました。さらにpackage.jsonには、次のようなoptionalDependenciesが注入されていました。

悪意あるoptionalDependenciesの例
{
  "optionalDependencies": {
    "@tanstack/setup": "github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c"
  }
}

このGitHub URLは一見TanStack公式に見えますが、実体はフォークネットワーク上の孤立コミットです。GitHubのフォーク間オブジェクト共有により、正規リポジトリURLのように見える形で参照できてしまいました。

Snykの分析によると、ペイロードは次のような情報を広範囲に収集します。

  • AWS、GCP、Azureなどのクラウド認証情報
  • Kubernetesサービスアカウントトークン
  • HashiCorpVaultトークン
  • ~/.npmrc、npmトークン、GitHubトークン
  • GitHubCLI設定、.git-credentials
  • SSH秘密鍵
  • .claude/.vscode/など開発ツール由来の設定

盗まれた情報は、Session/Oxen系の分散メッセンジャーネットワークやGitHub GraphQL経由のデッドドロップに流されました。さらに、npmレジストリの/-/v1/search?text=maintainer:<user>を使って、被害者が公開権限を持つ別パッケージを探し、同じ注入を行って自己複製します。

このため、TanStackだけで終わらず、Mistral AI、UiPath、OpenSearch関連パッケージなどにも波及したとSnykやUnit42は整理しています。

Mini Shai-Hulud/TeamPCPとの関係

Snykは、StepSecurityの帰属として、この攻撃をTeamPCPによるMini Shai-Hulud系列の攻撃として扱っています。Unit42も、2026年5月のMini Shai-Hulud継続波として、TanStack事件を「盗まれた認証情報なしで初期アクセスした波」と位置付けています。

Shai-Hulud系列は、2025年9月の自己増殖型npmワーム、2025年11月のShai-Hulud 2.0、2026年4月のMini Shai-Huludへと進化してきました。TanStack事件の重要性は、規模だけではありません。SLSA provenanceを突破して見える形の公開に成功したことで、「署名があるから安全」という運用前提を崩した点にあります。

SLSA provenanceの限界

SLSALevelsが保証する範囲と、しない範囲の整理

SLSA公式仕様では、Build L1はprovenanceの存在、Build L2はホストされたビルド基盤による署名付きprovenance、Build L3はより強い改ざん耐性を持つビルド基盤を求めます。目的は、成果物が期待されたソースとビルド手順から作られたことを検証しやすくすることです。

ただし、SLSAは「依存解決時に復元されたキャッシュが清潔だったか」「ワークフロー中に攻撃者コードが走っていなかったか」「ビルド済み成果物が悪意ある挙動をしないか」までを自動で保証するものではありません。

Unit42の表現を借りれば、Sigstoreは「TanStack/routerrefs/heads/mainからrelease.ymlがビルドした」と正しく証明しました。問題は、そのrelease.ymlの実行環境がすでに汚染されていたことです。

npm--provenanceに頼った検証の落とし穴

npmのprovenanceは有用です。npm公式ドキュメントにもある通り、パッケージがどこで、どのようにビルドされたかを公開し、Sigstoreの透明性ログに記録できます。

しかし、次のような判断は危険です。

  • npm audit signaturesが通ったから安全
  • provenanceがあるからレビュー不要
  • trusted publisherなら長期トークン侵害だけ見ればよい
  • SLSA Build Level3相当ならインストール時の挙動監視は不要

今回の教訓は、provenanceを捨てることではありません。provenanceは「どのパイプラインから来たか」を見るために使い、別レイヤーで「そのパイプラインが汚染されていないか」「パッケージが危険な挙動をしないか」を見る必要があります。

Sigstore/cosign/in-totoattestationとの関係

npm provenanceはSigstoreを利用します。SigstoreはOIDCに基づく短命証明書と透明性ログを使い、パッケージ公開時の来歴を検証可能にします。in-toto attestationは、ビルド手順や入力などを構造化して記録する土台です。cosignはコンテナやアーティファクト署名でよく使われるSigstore系ツールです。

この3つは「誰が、どの環境で、何を作ったか」を強くする技術です。一方で、「作られたものが安全か」は別問題です。今回のように、攻撃者コードが正規ビルド環境内で動けば、署名やattestationはその事実を正しく記録してしまいます。

自分のプロジェクトを確認する手順

npm ls @tanstack/router @tanstack/start

まず、直接依存と推移的依存を確認します。

TanStack関連依存の確認
npm ls @tanstack/router @tanstack/start

TanStack公式によると、影響はRouter/Start系の42パッケージです。実際の利用パッケージ名は@tanstack/react-router@tanstack/router-coreなどになることが多いため、次のように広めに確認します。

代表的な影響候補の確認
npm ls \
  @tanstack/react-router \
  @tanstack/vue-router \
  @tanstack/solid-router \
  @tanstack/router-core \
  @tanstack/react-start \
  @tanstack/router-plugin

npm lsは現在の依存ツリーを見るコマンドです。2026年5月11日に一時的に入って、その後消えたケースはlockfile履歴も見ないとわかりません。

npm audit signatures

npmの署名とprovenance検証も実行します。

署名とprovenanceの検証
npm audit signatures

ただし、今回の事件では「署名やprovenanceがある悪意あるパッケージ」が成立しました。このコマンドは必要ですが、単独では十分ではありません。

lockfileのgit log調査コマンド

2026年5月11日前後に依存更新が走っていないかを確認します。

lockfile履歴の確認
git log --since="2026-05-10" --until="2026-05-13" -- \
  package-lock.json yarn.lock pnpm-lock.yaml

差分内に@tanstack@tanstack/setuprouter_init.js、孤立コミット79ac49eedf774dd4b0cfa308722bc463cfe5885cが出てこないか見ます。

差分内のIOC検索
git log -p --since="2026-05-10" --until="2026-05-13" -- \
  package-lock.json yarn.lock pnpm-lock.yaml \
  | rg '@tanstack/setup|router_init|79ac49eedf774dd4b0cfa308722bc463cfe5885c|filev2\.getsession|seed[123]\.getsession'

作業環境やCIランナーに痕跡が残っていないかも確認します。

ローカル痕跡の確認
rg '@tanstack/setup|router_init|79ac49eedf774dd4b0cfa308722bc463cfe5885c' \
  package-lock.json yarn.lock pnpm-lock.yaml node_modules .npm .pnpm-store 2>/dev/null
 
test -f "$HOME/Library/LaunchAgents/com.user.gh-token-monitor.plist" && \
  echo "危険: gh-token-monitor LaunchAgentがあります"
 
test -f "$HOME/.config/systemd/user/gh-token-monitor.service" && \
  echo "危険: gh-token-monitor systemd user serviceがあります"

注意: SnykとUnit42は、GitHubトークン失効時にホームディレクトリ削除を試みるgh-token-monitor系の挙動を報告しています。該当する場合、トークンを失効する前に永続化ファイルを隔離する順序が重要です。

Renovate/Dependabot自動マージ設定の見直しポイント

自動更新Botは便利ですが、公開直後の悪意あるバージョンをそのまま取り込むと被害が広がります。

見直すポイントは次の通りです。

  • npm依存の自動マージを無条件にしない
  • 新規パッケージ、メジャー更新、インストールスクリプト追加は人間レビューに回す
  • lockfile差分にoptionalDependenciesのGitHub参照が突然増えたら止める
  • preinstallinstallpostinstall追加をCIで検出する
  • Renovateを使う場合は公開後の待機期間を設ける設定を検討する
  • Dependabot中心の運用では、ブランチ保護やCI側で待機・追加検査を補う

「テストが通ったら即マージ」では、今回のようなワーム型サプライチェーン攻撃には弱いです。テストは機能の破壊を見ますが、依存パッケージの公開直後リスクまでは見ません。

これからの防御策(3階層)

GitHubActions側:pull_request_target最小権限、permissionsジョブ単位、third-partyactionsのSHApinning、ephemeralrunner

GitHubActions側では、まずpull_request_targetでフォークコードをチェックアウトして実行しないことが最優先です。どうしても使う場合は、ラベル付け、コメント、メタデータ処理など、フォーク由来コードを実行しない用途に限定します。

次に、permissionsはワークフロー全体ではなくジョブ単位で最小化します。特にid-token:writeは、npm公開が本当に必要なジョブにだけ付与します。

third-partyactionsはタグではなくコミットSHAで固定します。TanStack公式も、攻撃後の改善としてthird-party action refsをSHA固定したと記載しています。

さらに、共有セルフホストランナーを使っている場合は、ジョブごとに破棄されるephemeral runnerへ寄せるべきです。キャッシュは高速化に効きますが、信頼境界をまたぐキャッシュは攻撃面にもなります。

npm公開側:mandatory2FA、trustedpublishers、reviewedpublishing、外部attestation併用

npm公開側では、mandatory 2FAとtrusted publishersは引き続き重要です。axios事件のような長期トークンやメンテナーアカウント侵害には、trusted publishingが強い防御になります。

ただし、TanStack事件はtrusted publishingの内側で起きました。そのため、次のような追加策が必要です。

  • 公開ジョブを他のビルド・テストジョブから分離する
  • 公開直前に成果物を別プロセスでレビューする
  • 予期しないワークフロー、ブランチ、ジョブ名からのprovenanceを拒否する
  • npm公開イベントを自前でも監視し、想定外のバージョン公開を即通知する
  • Sigstore/in-toto attestationに加え、成果物の内容差分や挙動検査を行う

「OIDCだから安全」ではなく、「OIDCを使う公開経路の中で任意コード実行を許していないか」を監査する必要があります。

依存側:lockfilelint、新規バージョンのcoolingperiod、Socket/Snyk/StepSecurityの比較

依存する側では、lockfileを防御境界として扱います。

  • CIではnpm installではなくnpm ciを使う
  • lockfileを必ずレビュー対象にする
  • GitHub URL依存、optionalDependencies、ライフサイクルスクリプトの追加を検出する
  • 公開直後の新バージョンをすぐ取り込まないcooling periodを置く
  • 依存更新Botの自動マージにセキュリティ検査を挟む

ツールは役割が少し違います。

ツール見るべき役割
Socket依存パッケージの振る舞い、怪しいスクリプト、突然の挙動変化検知に強い
Snyk脆弱性DB、悪意あるパッケージDB、依存ツリー管理に強い
StepSecurityGitHubActionsのワークフローリスク、CI/CD文脈の攻撃検知に強い

どれか1つで十分というより、CI/CD、npm依存、実行時挙動の3面で見るのが現実的です。

インシデント連鎖の全体像(TanStack5/11→node-ipc5/14→Mini Shai-Hulud 2nd wave)

今回の事件は単発ではありません。2026年5月は、npmサプライチェーン攻撃が連鎖しています。

日付事件何が変わったか
2026-05-11TanStack事件GitHubActions/OIDC/SLSA provenanceの信頼境界を突破
2026-05-14node-ipc事件メンテナーアカウント経由で複数メジャー系列へ同時投入
2026-05-19@antv波Unit42によると、約1時間で323パッケージ・639悪意あるバージョンへ拡大

StepSecurityのnode-ipc分析では、node-ipc@9.1.69.2.312.0.1が同時に悪意あるバージョンとして公開され、CommonJS bundleの末尾に約80KBの難読化ペイロードが追加されました。preinstallpostinstallではなく、require('node-ipc')した瞬間にIIFEが動くため、ライフサイクルフックだけを監視する検査を避けています。

Unit42は、5月19日の@antv波について、2026年5月のMini Shai-Hulud継続波として整理しています。TanStackで「正規CI/CDの中へ入り込む」手口が示され、node-ipcで「古い有名パッケージのメンテナー権限」が突かれ、さらに別エコシステムへ拡散していく構図です。

ここまで来ると、npmサプライチェーン攻撃は「たまに起きるパッケージ汚染」ではなく、公開基盤、CI/CD、開発端末、AIコーディングツール設定まで含む攻撃面になっています。

まとめ

TanStack事件の教訓は、SLSAやOIDCをやめよう、ではありません。むしろ逆です。長期トークンを減らし、provenanceを検証し、署名を使うことは必要です。

ただし、それだけでは足りません。

  • provenanceは「どこから来たか」を示すが、「安全な中身か」までは保証しない
  • trusted publishingは長期トークン侵害に強いが、正規ランナー内の任意コード実行には弱い
  • pull_request_targetは便利だが、フォークコード実行と組み合わせると危険になる
  • キャッシュは性能最適化だが、信頼境界をまたぐと攻撃経路になる
  • 依存更新は速さだけでなく、公開直後の待機と挙動検査が必要になる

axios事件では、メンテナーアカウント侵害と長期npmトークンのリスクが見えました。TanStack事件では、より進んだ防御を導入していても、CI/CD内部の信頼境界が破られると悪意あるパッケージが「正規の来歴」を持って出てくることが示されました。

これからの防御は、署名、来歴、ワークフロー最小権限、依存挙動検査、公開後監視を重ねる前提で考える必要があります。

参考リンク