PaPoo
cover
technews
Author
technews
世界の技術ニュースをリアルタイムでキャッチし、日本語でわかりやすく発信。AI・半導体・スタートアップから規制動向まで、グローバルテックシーンの「今」をお届けします。

TanStackのnpm侵害を読み解く:PR、GitHub Actions、OIDCがつながった“チェーン攻撃”の全貌

キーポイント

何が起きたのか

TanStack が公開した postmortem は、かなり生々しいです。
要するに、​GitHub の pull request を使った開発フローと、​GitHub Actions のキャッシュ、そして npm の trusted publishing が、攻撃者にとって都合よくつながってしまった、という話です。

image_0003.svg

2026-05-11 の 19:20〜19:26 UTC の間に、攻撃者は 42 個の npm パッケージに対して合計 84 個の悪意あるバージョンを公開しました。
しかも厄介なのは、これが単なる「npm アカウント乗っ取り」ではなく、​GitHub Actions の実行環境上で OIDC token を抜き取り、その権限で npm に直接 publish した形だということです。

image_0004.svg

ここ、かなり面白くて、同時に背筋が寒いところです。
「npm の秘密鍵が漏れたわけではない」のに、結果としては正規の公開操作に見える形で悪性パッケージが流通した。これ、サプライチェーン攻撃のいやらしさが全部詰まっています。

悪意あるパッケージは何をしたのか

影響を受けたバージョンを npm installpnpm installyarn install すると、以下のような動きが走りました。

image_0005.svg

image_0006.svg

この「自分を増やす」動きがかなり悪質です。
単に情報を抜くだけではなく、​次の被害者を増やすための足場まで作りにいっている。個人的には、ここが一番イヤなポイントだと思います。被害が“感染”っぽく広がるからです。

image_0007.svg

どうやって侵入したのか

攻撃は、ざっくり3段階です。

1. pull_request_target を悪用した

GitHub Actions の pull_request_target は、​PR の変更内容に対して、ベースリポジトリ側の権限で動く仕組みです。
本来は、ラベル付けやコメントなどの「安全な作業」に向いています。

image_0008.svg

ところが今回、TanStack の bundle-size.yml はこの pull_request_target 上で、​PR のマージ後コードを checkout して build を実行していました。
つまり、​外部から来たコードを、ベースリポジトリの文脈で実行してしまったわけです。

image_0009.svg

ここで大事なのは、pull_request_target 自体が悪いのではなく、​何を実行するかです。
コメントだけならまだしも、build や install を走らせると、急に話が危なくなる。GitHub Actions は便利ですが、便利さの裏に「権限の境界」があるんですよね。

2. GitHub Actions の cache を汚染した

攻撃者は、PR 内で悪意あるコードを実行し、​pnpm store に細工しました。
そのあと actions/cache がその汚染済みキャッシュを保存してしまい、​後続の main ブランチの workflow がそれを復元してしまった、という流れです。

image_0010.svg

GitHub Actions の cache は、ざっくり言うと「ビルド結果や依存関係を保存して次回を速くする仕組み」です。
でもこれは裏を返すと、​一度混ざった毒を、次の実行に持ち越せるということでもあります。

image_0011.svg

ここは本当に教訓的です。
キャッシュって普通は“善意の時短装置”なんですが、信頼境界をまたぐと毒の保冷庫になるんですよね。かなり皮肉が効いています。

image_0012.svg

3. OIDC token を runner のメモリから抜いた

最後の決め手がこれです。
release workflow には id-token: write が設定されていて、これは npm の OIDC trusted publishing に必要です。
OIDC は、短命のトークンで安全に外部サービスへ認証する仕組みで、セキュリティ的にはかなり良い考え方です。

ただし、攻撃者が runner 上でコードを実行できる状態だと、そのトークンをメモリから抜き取られる可能性がある。
今回の悪性コードは、GitHub Actions runner のプロセスを探して、/proc を読みにいき、メモリから OIDC token を取り出して、​npm registry に直接 POSTしました。

image_0013.svg

つまり、workflow の「Publish Packages」ステップを通らなくても、​publish 権限そのものを奪ってしまったわけです。

image_0014.svg

ここは正直、かなり巧妙です。
「ワークフローの中の publish step を守れば安心」と思いがちですが、実際にはrunner 上で実行できるコードがある時点で、認証トークンの扱い全体が危うい。クラウド時代のセキュリティって、本当に“実行環境そのもの”が攻撃対象になるんだな、と感じます。

image_0015.svg

なぜここまで連鎖できたのか

TanStack の説明では、この攻撃は 3つの問題が鎖のようにつながって成立したと整理されています。

image_0016.svg

それぞれ単体なら、すぐに大事故にならないかもしれません。
でも、​境界をまたぐたびに信頼が少しずつ崩れると、最後には「正規の publish に見える malicious publish」が成立してしまう。
このタイプの攻撃は、見た目が地味なのに被害が派手で、かなり厄介です。

image_0017.svg

どうやって見つかったのか

検知は、​外部の研究者による発見が先でした。
ashishkurmi が公開 issue に詳しい分析を投稿し、かなり早い段階で広く共有されました。TanStack 側もすぐに動き、npm security への連絡、該当パッケージの deprecate、GitHub の権限整理、キャッシュの削除、ワークフローの修正などを進めています。

この「外部研究者が先に見つけた」という事実は、少し複雑です。
もちろん事故は起きない方がいい。でも現実には、オープンソースの巨大な供給網では、​コミュニティの目が最後の防波堤になることがある。これは健全さでもあり、しんどさでもあると思います。

image_0018.svg

影響を受けた人はどうすべきか

TanStack は、影響を受けたバージョンをインストールした可能性がある人に対して、​インストールホストから到達可能な認証情報をローテーションするよう強く勧めています
具体的には次のようなものです。

image_0019.svg

image_0020.svg

ここで大事なのは、「npm を入れ直せば終わり」ではないことです。
もし悪性パッケージがインストール時に認証情報を抜いていたら、​そのマシンから使える秘密情報は全部見直し対象です。
これは面倒ですが、サプライチェーン攻撃の後処理としては避けられません。

個人的にここが重要だと思う点

この件で一番学びが大きいのは、​**“安全そうに見える便利機能”が、つながると危険になる**ということだと思います。

image_0021.svg

image_0022.svg

でも、これらはそれぞれ単体ではなく、​組み合わせたときの信頼境界を見ないといけない。
セキュリティって、結局「この機能は危険か?」ではなく、​**“どこまで信用していい前提で動いているか”**の設計なんですよね。

image_0023.svg

TanStack の postmortem は、そのことをすごくわかりやすく示していると思います。
しかもこれは TanStack だけの話ではなく、GitHub Actions を使う多くのプロジェクトにそのまま刺さる話です。
便利なCIほど、雑に組むと一気に危険になる。そこは本当に気をつけないといけません。

まとめ

今回の事件は、単なる「パッケージが汚染された」では終わりません。
PR、CI cache、OIDC、npm publish が全部つながって、サプライチェーン全体が攻撃の入り口になったのが本質です。

image_0024.svg

しかも怖いのは、攻撃が派手なゼロデイではなく、​既知のパターンや既知の手口の組み合わせで成立していること。
つまり、「珍しい脆弱性がなくても、設計の継ぎ目を突けばここまで行ける」ということです。これはかなり現実的で、だからこそ学ぶ価値が大きい事件だと思います。

image_0025.svg


参考: Postmortem: TanStack npm supply-chain compromise | TanStack Blog

同じ著者の記事