2026年5月、GitHubは自社の内部リポジトリへ不正アクセスがあったことを公表した。GitHubの内部へ到達した直接の経路は、従業員が使っていたVisual Studio Codeの拡張機能であり、その拡張機能が汚染された経緯をさかのぼると、別の組織で起きたサプライチェーン侵害に行き着く。この記事では、GitHub公式ブログとGitHub Security Advisoryの発表内容をもとに何が起きたのかを整理し、あわせて、同じことが個人や中小企業の自前サーバで起きた場合との違いを見る。

この記事でわかること

  • GitHubが公表した侵害の概要と対応の時系列
  • VSCode拡張機能が乗っ取られ、配布されるまでの経緯
  • 攻撃者が使った認証情報の窃取と社内リポジトリ複製の手口
  • 侵害の痕跡を確認するための具体的な確認方法
  • 個人や中小企業が自前サーバにGitLabやGiteaを置いた場合との防御の違い

1. GitHubで何が起きたのか

発表の概要

GitHubは2026年5月20日、公式セキュリティブログで次のように説明した。5月18日(月)、第三者が公開した悪意のあるVSCode拡張機能によって従業員の端末が侵害されたことを検知し、封じ込めた。該当する拡張機能のバージョンはその後削除されている。

現時点の評価では、持ち出しが確認されているのはGitHub内部のリポジトリである。攻撃者が主張する約3,800件という件数は、GitHub自身の調査結果と方向性として一致している。GitHubの内部リポジトリの一部には、サポート対応でのやり取りの抜粋など、顧客に関する情報が含まれるものもある。ただし、顧客自身が持つエンタープライズやリポジトリなど、内部リポジトリの外側にある顧客情報への影響を示す証拠は、現時点で確認されていない。

GitHubは月曜から火曜にかけて、影響の大きい認証情報を優先しながら重要な鍵の入れ替えを行った。

全体の流れを単純化すると、次のようになる。

図 1

対応の時系列

GitHubの公表内容を時系列でまとめると、次のようになる。

日付 出来事
2026-5-18(月) 従業員端末の侵害を検知・封じ込め
2026-5-18〜19(月〜火) 影響の大きい認証情報を優先しながら、重要な鍵のローテーションを実施
2026-5-20 公式ブログで最初の発表。内部リポジトリのみが対象という暫定評価を公表
2026-5-26 GitHub Enterprise Serverの署名鍵ローテーションを発表

GitHubは、調査が完了した段階であらためて詳細な報告を公表するとしている。

2. 攻撃の技術的な仕組みと問題点

ここからは、GitHub Security Advisory(GHSA-c9j4-9m59-847w、拡張機能の開発元であるnrwl社が公開)の内容をもとに、攻撃がどのように成立したかを技術的に見ていく。

侵害の発端

この拡張機能の開発元によると、根本原因は開発者のひとりが別のサプライチェーン侵害(Tanstackの侵害)に巻き込まれたことにある。この侵害によってGitHub CLI(ghコマンド)経由の認証情報が流出し、攻撃者はこの認証情報を使ってnx-consoleリポジトリ上でコントリビューターとしてワークフローを実行できる状態になった。

当時、この拡張機能の組織では、特定の1人が承認なしに新しいバージョンを公開できる権限を持っていた。この権限が悪用され、攻撃者は正規の公開経路を使って悪意のあるバージョンを配布した。

拡張機能の乗っ取りと配布

悪意のあるバージョン(Nx Console 18.95.0)は、Microsoftの拡張機能マーケットプレイスでは協定世界時12時30分に公開され、12時48分に削除されるまでの約18分間、入手可能な状態だった。もう一つの配布経路であるOpenVSXでは、12時33分に公開され13時09分に削除されるまでの約36分間、入手可能だった。

VSCodeは既定で拡張機能を自動更新するため、この短い時間の間に多くの端末へ配信された。マーケットプレイス側の記録上のダウンロード数はVSCode Marketplaceで28件、OpenVSXで41件と少数だったが、開発元自身の分析では、攻撃発生から2日間でVSCodeから約6,000件、Cursorから1件の起動が確認されている。記録上のダウンロード数と、実際に動いた端末の数は一致しない。

認証情報の窃取と持続化

配布された拡張機能は、難読化されたペイロードを取得し、端末上の複数の場所から認証情報を収集した。対象にはVaultのトークン、npmのトークン、AWSの認証情報(IMDS/ECSメタデータ、Secrets Manager、SSM等)、GitHubのトークンおよびSSH鍵、稼働中であれば1Passwordの保管内容、そのほかファイルシステム上の秘密鍵や接続文字列が含まれる。収集した情報はHTTPS、GitHub API、DNSの3経路で外部へ送信された。永続化については、macOS環境ではLaunchAgentの設置、Linux環境ではsudoersファイルへの書き込みが試みられている。

端末上で発生していた動きを整理すると、次のようになる。拡張機能が起点となり、複数の保管場所から認証情報を集め、まとめて3つの経路で外部へ送信していた。どの認証情報がどの経路で送られたかまでは公表されていないため、収集と送信は分けて描いている。

図 2

窃取された認証情報の中にGitHub従業員の端末に到達するものが含まれていたことで、攻撃者は最終的にGitHubの内部リポジトリへアクセスできる状態になった。

図 3

何が問題だったのか

今回の一連の流れから、少なくとも次の3点が問題として挙げられる。

1件目は、拡張機能の公開に第二承認者を必要としない運用があったことである。開発元はこの問題を認識し、対応後は2人の管理者による承認を必須とする体制に変更した。

2件目は、拡張機能マーケットプレイスの自動更新に、公開から配信までの審査期間や猶予期間が存在しないことである。今回、悪意のあるバージョンが入手可能だった時間はわずか18分から36分だったが、この短さは被害を防ぐ材料にはならず、逆に多数の端末へ配信されるのに十分な時間だった。

3件目は、信頼の連鎖が複数の組織をまたいで長くつながっていたことである。攻撃の起点は、GitHubとは別の組織で起きたTanstackの侵害だった。そこから漏れた認証情報が別の開発者に渡り、その開発者が管理する拡張機能が汚染され、その拡張機能を使っていたGitHubの従業員の端末に到達し、最終的にGitHubの内部システムにまで影響が及んだ。一つひとつの組織の対策が機能していても、組織をまたいだ信頼の連鎖全体を見なければ、こうした侵入経路は見えにくい。

侵害の痕跡を確認する

GitHub Security Advisoryには、侵害の痕跡(Indicators of Compromise)として、特定のファイルの存在や特定のプロセスの稼働が挙げられている。該当するNx Console 18.95.0を導入していた場合は、以下のような方法で確認できる。次のスクリプトはmacOSとLinux向けである。Advisoryにはこのほかに、Windowsでの %USERPROFILE%\.bun\bin\bun.exe が挙げられている。

#!/usr/bin/env bash
# Nx Console 18.95.0 の侵害痕跡を確認するスクリプト
# 出典: GitHub Security Advisory GHSA-c9j4-9m59-847w

echo "== 痕跡ファイルの確認 =="
for f in \
  "$HOME/.local/share/kitty/cat.py" \
  "$HOME/Library/LaunchAgents/com.user.kitty-monitor.plist" \
  "/var/tmp/.gh_update_state"
do
  if [ -e "$f" ]; then
    echo "検出: $f"
  fi
done

echo "== 該当プロセスの確認 =="
ps aux | grep -E "cat\.py|__DAEMONIZED=1" | grep -v grep

実行例(痕跡が見つからない場合)は次のようになる。

== 痕跡ファイルの確認 ==
== 該当プロセスの確認 ==

いずれの項目にも出力がなければ、少なくとも上記の痕跡は見つからなかったことになる。痕跡が無いことは、侵害されていないことの証明にはならない。 痕跡が見つかった場合は、拡張機能を修正版(18.100.0以降)へ更新したうえで、該当プロセスの停止、痕跡ファイルの削除、到達可能な範囲の認証情報の入れ替えを行う必要がある。

3. これは防げたのか

ここまで見た問題点に対して、開発元は2人の管理者による承認を必須とする体制へ変更した。一見すると再発防止策のように見えるが、この種の対策がどこまで有効なのかを、GitHub自身のこれまでの動きと照らして考えてみる。

積み重ねられてきた対策

GitHubは今回の事件より前から、npmを中心としたサプライチェーン攻撃への対策を段階的に発表・実施してきた。発表の時点では計画だったものが後から実装された例もあるため、両者を分けて並べる。

時期 段階 内容
2025-9 発表(計画) npmの公開に二要素認証を必須とし、公開用トークンを7日間で失効する形へ移す方針を示した
2025-12 発表(計画) Shai-Huludなど連鎖的なマルウェア配布への対応として、公開前にレビュー期間を設ける「段階的公開」を予告した
2026-4〜7 実施 段階的公開の開始(2026-5)、npmのインストールスクリプトの既定無効化(2026-6)、GitHub Actionsの実行ポリシー追加(2026-6)、Dependabotの更新待機(2026-7)などを順次投入した

これらはすべて、公開の経路を絞り、認証を強め、レビューの猶予を設けるという方向性の対策である。今回、開発元が導入した「2人承認」も同じ方向性の対策であり、目新しいものではない。

それでも起きたこと

今回の侵害は、npmではなくVSCode拡張機能のマーケットプレイスという別の経路で起きた。時期も重なっている。npmで段階的公開が始まったのは2026年5月であり、今回の事件と同じ月にあたる。攻撃者はさらにその手前で、開発者個人のGitHub CLI認証情報を、また別のサプライチェーン侵害(Tanstack)から窃取していた。つまり、ある経路を固めるたびに、対策の対象になっていない別の経路が使われている。

そして「2人承認」という対策自体にも同じ弱点が潜んでいる。承認者を2人に増やしても、その2人の認証情報が窃取されれば、承認の仕組みごと突破される。今回の攻撃で実際に窃取された対象には、GitHubのトークンやSSH鍵、CLIの認証情報が含まれていた。承認者を増やすという対策は、まさにその種の窃取を防ぐことを目的としていない。

結局、防げるのか

ここまでの事実を並べると、次のような見方も成り立つ。対策は常に「直前に使われた手口」を塞ぐ形で追加され、攻撃者は塞がれていない別の経路へ移動する。認証を強めれば認証情報そのものが狙われ、承認者を増やせば承認者の認証情報が狙われる。少なくとも公開されている範囲では、対策は個別の経路をひとつずつ塞ぐ形で進んでおり、経路の総数が減っているかどうかは判断できない。

これは今回の事件に限った話ではなく、npmを含むサプライチェーン攻撃全般で繰り返し観測されてきたパターンでもある。次にどの経路が使われるかを事前に言い当てることは難しく、対策の発表と新しい手口の発生が交互に続いている状態が、少なくとも公開されている情報からは読み取れる。

4. GitHubの防御の構成(公開情報から分かる範囲)

GitHubは内部システムの実際のネットワーク構成そのものは公開していない。ここで示すのは、GitHubが公式ブログや公式ドキュメントで説明している防御機能を、公開されている情報の範囲内で層として整理したものであり、実際の内部構成図ではない。

公開情報から整理した構成

公開されている情報からは、おおむね次の4層が確認できる。パッケージや拡張機能の公開・プッシュといった入り口の層、二要素認証や有効期限付きトークンによる認証・権限の層、シークレットスキャンやマルウェア検知、段階的公開レビューといった検知の層、そして検知後に認証情報をローテーションする対応の層である。

次の図は層の並びを示したものであって、処理が上から下へ流れる順序ではない。 また、各層に挙げた機能の適用範囲はそれぞれ異なる(多くはnpmを対象に整備されたものである)。

図 4

この構成図と今回の事件の関係

今回の侵害経路をこの構成に当てはめると、次のことが分かる。攻撃者が最初に得たのはGitHub CLIの認証情報であり、これは「認証と権限」の層が守ろうとしている対象そのものである。悪意のある拡張機能が配布されたのは「入り口」の層のうち、GitHubが直接管理していないVSCode拡張機能マーケットプレイスだった。「検知の仕組み」の層にあるマルウェア検知や段階的公開レビューは、主にnpmを含むパッケージエコシステムを対象に整備されてきたものであり、今回の拡張機能マーケットプレイスは対象範囲の外にあった。結果として、事件が表面化したのは「検知後の対応」の層、つまり認証情報のローテーションと鍵の入れ替えの段階だった。

3章で見た「対策が塞ぐ経路と、攻撃者が使う経路がずれる」という構図は、この4層構成の中でも同じ形で表れている。

5. 同じことが個人や中小企業で起きたらどうなるか

ここまではGitHub側の話である。では、GitHubを使わず、自前のサーバにGitLabやGiteaを置いた場合はどうなるのか。防御の仕組みの違いを、体の不調にたとえて整理する。

危険の種類を3つに分ける

たとえ 実際に起きること 特徴
風邪 汚染された拡張機能やパッケージが、開発者の端末に入る 感染源が常に周りにいて、誰でもかかる。今回の事件はこれにあたる
怪我 インターネットへ公開したサーバが、無差別の探索や攻撃を受ける 人通りの多い場所に立つほど確率が上がる
持病 更新を当てていないソフトを、そのまま動かし続ける 自覚症状が出ないまま進む。止まっていないので気づかない

ひとつ補足しておく。風邪や怪我と違い、攻撃には意図を持った相手がいる。運が悪ければ起きるのではなく、狙う側が常に探し続けている状態が前提になる。ここでのたとえは種類を分けるためのもので、確率の話ではない。

4つの層を自前サーバに置き換える

4章で整理したGitHubの4層を、自前サーバの場合と並べると次のようになる。

GitHub 自前のGitLab/Gitea
入り口 常にインターネットへ公開されている。閉じるという選択肢がない 公開するかどうかを自分で決められる。社内からのみに閉じれば、この層の入り口は大きく減る
認証と権限 二要素認証の必須化や短命トークンを、提供側の判断で対象の利用者へ一斉に適用できる(npmの公開などが該当) 二要素認証の仕組みはあるが、全員に行き渡らせるのは自分の運用次第
検知の仕組み シークレットスキャンやマルウェア検知が提供されている(適用範囲は公開設定や契約によって変わる)。段階的公開レビューも2026年5月からnpmで始まっている 素の導入では動かない。近いものを用意するには追加の設定や外部ツールの組み合わせが要る
検知後の対応 検知の翌日までに、影響の大きい認証情報から鍵の入れ替えが行われた 手順と担当を決めていなければ動かない

体にたとえると、入り口の層は「人混みに出るかどうか」、認証と権限の層は「予防接種」、検知の仕組みの層は「定期健診」、検知後の対応の層は「かかりつけ医」にあたる。

自前サーバは、人混みに出ないことを選べる。これはGitHubには選べない。一方で、定期健診とかかりつけ医は最初からは付いてこない。

今回の事件は、自前サーバなら防げたか

防げない。今回GitHubの内部リポジトリへ到達した経路は、サーバ側ではなく開発者の端末だったためである。

図 5

2章で見たとおり、拡張機能が端末から集めた対象には、GitHubのトークンやSSH鍵のほか、ファイルシステム上の秘密鍵や接続文字列が含まれていた。置き場所を自前サーバへ移した場合、端末に置かれるものが自前サーバの鍵に変わるだけで、集められること自体は変わらない。

違うのはその後である。GitHubでは5月18日に検知と封じ込めが行われ、重要な鍵の入れ替えは翌19日までに終えている。自前サーバで同じ流れを踏むには、端末側の異常に気づく手立てと、鍵を入れ替える手順の両方が用意されている必要がある。用意がなければ、持ち出されたことに気づかないまま時間が過ぎる。

かかる病気は同じで、違うのは見つかるまでの時間である。

自前サーバが有利な点と、その代償

有利な点は「入り口」の層に限られる。外部からの接続を受けない構成にすれば、無差別の探索や攻撃、つまり怪我の確率は下がる。これはGitHubを含む公開サービスには選べない形である。

代償は、残りの3層がすべて自分の仕事になることである。

  • 更新を当て続ける(止めれば持病になる)
  • バックアップを取り、戻せることを確認する
  • 誰がどこまで見られるかを管理し、退職時に権限を消す
  • 異常に気づく手立てを用意する
  • 止まったときは、自分が直すまで全員が止まる

条件が最も悪いのは、外部からの接続を受けたまま、これらが回らない構成である。人混みに出ながら、健診も予防接種も受けない状態にあたる。

規模は関係あるか

怪我については、規模で守られる部分はない。インターネットへ公開したサーバは、公開した時点で誰からでも到達できる状態になる。攻撃側が対象を選ぶ条件は公表されていないが、少なくとも「規模が小さいので到達されない」という状態は作れない。

風邪についても関係ない。汚染された拡張機能やパッケージは利用者を選ばずに配信される。今回、GitHubの従業員の端末が侵害されたことがそれを示している。

規模によって変わるのは、かかった後に使える手当ての量である。専任の担当がいるか、検知の仕組みが動いているか、鍵を入れ替える手順が決まっているか。ここが、GitHubと、個人や中小企業の自前サーバとの実際の差になる。

まとめ

今回の事件は、GitHub自身が直接侵害されたのではなく、別の組織で起きたサプライチェーン侵害の影響が、開発ツールの自動更新という経路を通じて段階的に伝わった結果だった。GitHubは内部リポジトリのみが対象という暫定評価を示し、認証情報のローテーションとGitHub Enterprise Serverの署名鍵ローテーションという対応を取っている。

一つの拡張機能が入手可能だった時間はわずか十数分から数十分だったが、自動更新という仕組みと、組織をまたいだ信頼の連鎖の長さが、被害の範囲を広げた。対策はそのつど積み重ねられているが、今回のように対策の対象外だった経路が使われた事例が続いている以上、対策が経路を塞ぐ速度と、攻撃者が新しい経路を見つける速度のどちらが上回るのかは、この記事の時点では判断できない。

この事件を受けて、GitHubをやめて自前のサーバへ移すという判断も出てくる。ただし5章で見たとおり、今回の侵入経路は開発者の端末であり、置き場所を変えても端末から認証情報が集められること自体は変わらない。自前サーバで変わるのは、外部からの接続を受けるかどうかという入り口の条件と、異常に気づくまでの時間である。前者は自分で選べるが、後者は検知の仕組みと担当を自分で用意しなければ短くならない。