
【サプライチェーン攻撃】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/setup、router_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.1とaxios@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:49 | PR#7378「WIP:simplify history build」を作成 |
| 2026-05-11 11:11 | pull_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:26 | 2回目のリリースランで残りの悪意あるバージョンが公開 |
| 2026-05-11 19:46 | StepSecurity所属の研究者ashishkurmiがIssue#7383で報告 |
| 2026-05-11 20:19 | 最初の2バージョンが非推奨化 |
| 2026-05-11 21:03 | 全84バージョンの非推奨化が完了 |
| 2026-05-11 22:13〜23:55 | npmセキュリティチームが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": {
"@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/routerのrefs/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
まず、直接依存と推移的依存を確認します。
npm ls @tanstack/router @tanstack/startTanStack公式によると、影響は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-pluginnpm lsは現在の依存ツリーを見るコマンドです。2026年5月11日に一時的に入って、その後消えたケースはlockfile履歴も見ないとわかりません。
npm audit signatures
npmの署名とprovenance検証も実行します。
npm audit signaturesただし、今回の事件では「署名やprovenanceがある悪意あるパッケージ」が成立しました。このコマンドは必要ですが、単独では十分ではありません。
lockfileのgit log調査コマンド
2026年5月11日前後に依存更新が走っていないかを確認します。
git log --since="2026-05-10" --until="2026-05-13" -- \
package-lock.json yarn.lock pnpm-lock.yaml差分内に@tanstack、@tanstack/setup、router_init.js、孤立コミット79ac49eedf774dd4b0cfa308722bc463cfe5885cが出てこないか見ます。
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参照が突然増えたら止める preinstall、install、postinstall追加を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、依存ツリー管理に強い |
| StepSecurity | GitHubActionsのワークフローリスク、CI/CD文脈の攻撃検知に強い |
どれか1つで十分というより、CI/CD、npm依存、実行時挙動の3面で見るのが現実的です。
インシデント連鎖の全体像(TanStack5/11→node-ipc5/14→Mini Shai-Hulud 2nd wave)
今回の事件は単発ではありません。2026年5月は、npmサプライチェーン攻撃が連鎖しています。
| 日付 | 事件 | 何が変わったか |
|---|---|---|
| 2026-05-11 | TanStack事件 | GitHubActions/OIDC/SLSA provenanceの信頼境界を突破 |
| 2026-05-14 | node-ipc事件 | メンテナーアカウント経由で複数メジャー系列へ同時投入 |
| 2026-05-19 | @antv波 | Unit42によると、約1時間で323パッケージ・639悪意あるバージョンへ拡大 |
StepSecurityのnode-ipc分析では、node-ipc@9.1.6、9.2.3、12.0.1が同時に悪意あるバージョンとして公開され、CommonJS bundleの末尾に約80KBの難読化ペイロードが追加されました。preinstallやpostinstallではなく、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内部の信頼境界が破られると悪意あるパッケージが「正規の来歴」を持って出てくることが示されました。
これからの防御は、署名、来歴、ワークフロー最小権限、依存挙動検査、公開後監視を重ねる前提で考える必要があります。
参考リンク
- Postmortem: TanStack npm supply-chain compromise | TanStack Blog
- TanStack npm Supply Chain Attack: Detailed Analysis of the May 2026 GitHub Actions Breach and Multi-Ecosystem Impact | Rescana
- TanStack npm packages compromised: inside the Mini Shai-Hulud supply chain attack | Snyk
- Active Supply Chain Attack:Malicious node-ipc Versions Published to npm | StepSecurity
- Shai-Hulud 2.0: Guidance for detecting, investigating, and defending against the supply chain attack | Microsoft Security Blog
- The npm Threat Landscape: Attack Surface and Mitigations | Unit42
- Supply Chain Compromise of axios npm Package | Huntress
- SLSA v1.1 Security levels | SLSA
- Generating provenance statements | npm Docs
- npm audit signatures | npm Docs