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

VSCodeの落とし穴でGitHubトークンが盗まれる話をわかりやすく解説

キーポイント

まず何が起きる話なのか

この記事は、​​「VSCodeのバグを使うと、1クリックでGitHub token を抜かれるかもしれない」​という、かなりゾッとする話を扱っています。

ここでいう GitHub token は、ざっくり言うと GitHub上であなたの代わりに操作するための合鍵 みたいなものです。
しかも記事で問題にしている token は、単なる読み取り専用ではなく、​private repository を含めて読み書きできる可能性がある。これは普通に危ないです。かなり危ない。

対象になるのは、GitHub の github.dev
URL の github.comgithub.dev に変えると、ブラウザ上で動く軽量版VSCodeが開きます。コードを見たり編集したり、Pull Request を出したり、コミットしたりできるので、見た目以上にパワフルです。

image_0001.png

私の感想を言うと、こういう「便利さのために権限をまとめて持たせる」設計は、うまくハマると最高なんですが、ひとたび穴が空くと被害が一気に大きくなるのが怖いところです。まさにその典型に見えます。

なぜGitHub token がそんなに強いのか

記事のポイントは、github.dev が GitHub と連携するために OAuth token を使っていることです。
OAuth token は、アカウントに紐づいた「このアプリに操作を許可するための鍵」です。

しかも記事によると、この token は 特定の1つのrepoだけに限定されていない
つまり、あなたがアクセスできる他のrepoにも届いてしまう。これ、地味にではなくかなり強い権限です。

image_0002.webp

だから、もし攻撃者がこの token を手に入れたら、単に「この画面を見られる」どころでは済まない可能性があります。
リポジトリの閲覧、変更、場合によっては秘密情報へのアクセス まで視野に入ってしまうわけです。

VSCodeの webview とは何か

この記事で鍵になるのが webview です。
webview は、VSCodeの中で使われる「埋め込みWeb画面」みたいなものです。

たとえば以下のような場面で使われます。

image_0003.png

セキュリティ上、webview はメインのVSCode画面とは 別の origin で動きます。
origin というのは、ざっくり言えば「どのサイト由来のページか」を区別する仕組みです。別 origin だと、互いの中身を勝手に触れません。

これはとても大事です。
もし webview の中で動くJavaScriptが、VSCode本体に自由に触れたら、​ほぼリモートコード実行 になりかねないからです。Electron アプリは特にそこが怖い。

なので、VSCodeは webview を隔離して、安全に表示だけさせるようにしている。
ここまでは、かなり真っ当な設計です。むしろ「よく考えてある」と言いたい。

image_0004.png

でも、その隔離が別の問題を生んだ

問題は、webview が完全に孤立していると使い勝手が悪すぎることです。

たとえば、Markdown preview で「今どの行を選択しているか」を反映したいとか、編集内容に応じてプレビューを更新したいとか。
こういう機能は、メイン画面と webview が会話しないと実現できません。

そこで使われるのが window.postMessage() です。
これは 別 origin のウィンドウ同士が、安全にメッセージを送り合う仕組み です。

image_0005.png

つまり、VSCode本体が

{ "type": "onDidChangeTextEditorSelection", "line": 31 }

みたいなメッセージを webview に送り、webview 側がそれを受けてハイライトを付ける、という流れです。

ここまでは便利です。かなり便利。
でも便利な仕組みは、たいてい便利なぶんだけ危うい。

image_0006.png

バグの正体: キーボード入力の転送

記事の核心はここです。

webview 内で Ctrl + Shift + P を押すと、VSCodeのコマンドパレットが開きます。
これは、webview 内にフォーカスがあっても、VSCodeのショートカットが効くようにするための配慮です。ユーザー体験としては親切です。

そのために webview 側では、keydown を拾って親側へ did-keydown として送っています。
要するに、​webview が受けたキー入力を、VSCode本体に中継している わけです。

image_0007.png

ここがまずい。

なぜなら、webview 内で動く攻撃者のJavaScriptが、この仕組みを使って ​「ユーザーが押したことにして」キー入力を偽装できる からです。
本来はユーザーの操作であるべき入力が、スクリプトから送れてしまう。これはかなり嫌な匂いがします。

私の印象では、ここは「機能を優先した結果、境界が少し曖昧になった」タイプの事故に見えます。
安全な境界線を越えるのは一瞬なのに、その一瞬で被害が跳ね上がる。セキュリティあるあるですが、やっぱり怖いです。

どこまで悪用できるのか

image_0008.png

攻撃者がやりたいことは、最終的には 任意のコード実行 です。
その入口として、VSCodeのコマンドや拡張機能の仕組みを悪用しようとしています。

ただし、ここは思ったほど単純ではありません。

1. コマンドパレットを開く

Ctrl + Shift + P でコマンドパレットを表示できます。

2. 文字入力は意外と難しい

問題は、単に「キーイベントを送る」だけでは、コマンドパレットに文字をちゃんと入力できないケースがあることです。
ブラウザやVSCodeの実装の都合で、​見た目はキーを押していても入力欄に反映されない ことがある。

ただ、上下キーや Enter は使えます。
そこで、攻撃者は キーボードショートカットで操作できる機能 を探します。

image_0009.png

3. 通知を利用する

記事では、Ctrl + Shift + A に割り当てられた ​「通知の主要アクションを実行する」​ 機能を使う方法が紹介されています。

VSCodeは、ワークスペースが .vscode/extensions.json に書いた推薦 extension を通知します。
そして、その通知に対してショートカットで「OK」「Install」みたいな操作ができる。

ここまで聞くと、「じゃあ悪意ある extension をおすすめとして置けば終わりでは?」と思いますよね。
実際、かなりそれに近いです。ただし、最新のVSCodeには publisher trust system という仕組みがあり、初めて見る publisher の extension を入れるときは、追加の確認が入ります。

4. そこで local workspace extensions を狙う

記事では次の抜け道として、​local workspace extensions が使われています。

image_0011.png

trusted workspace 内なら、.vscode/extensions に置かれた extension を直接インストールできる。
しかも、この経路では publisher trust の確認をすり抜けられる場合がある。

ただし、そこでもまた別の壁が出ます。
web版VSCode側の CSP(Content Security Policy、ざっくり言うと「読み込んでいい資源を制限するルール」)に引っかかるのです。
ローカルのextensionをそのまま動かすのは、まだすんなりいかない。

5. keybindings を悪用する

そこで記事はさらに一段ひねります。
extension の package.json追加の keybinding を定義して、任意のVSCodeコマンドを発火させるのです。

つまり、攻撃者が狙うのは「コードを直接置く」だけではなく、​VSCodeの正規の機能を組み合わせて、最終的に危険な操作へ誘導すること です。
この発想、正直かなり巧妙です。単純な脆弱性というより、​機能の継ぎ目をぬっていく攻撃 という感じがします。

image_0012.png

攻撃の流れをざっくり言うと

記事の内容を、なるべく平たく並べるとこうです。

  1. 攻撃者が仕込んだ repo を開かせる
  2. そこに Jupyter notebook を入れておく
  3. notebook の中のHTML/JavaScriptでコードを実行させる
  4. webview の keydown 中継を悪用して、VSCodeのショートカットを押したことにする
  5. 通知を操作して extension 関連の処理を進める
  6. 追加の keybinding を使って、より危険なコマンドを実行させる
  7. 結果として、GitHub token や、場合によってはより深い権限を奪われる可能性がある

この攻撃がイヤらしいのは、​​「明らかなマルウェアをダウンロードさせる」感じではなく、見た目は普通のVSCode操作に見えること です。
ユーザーからすると、ただWeb版VSCodeでrepoを開いただけ。なのに裏で勝手にショートカットが押されていく。かなり不気味です。

image_0013.png

何が重要な教訓なのか

この話の重要ポイントは、単に「VSCodeにバグがあった」というだけではありません。

1. 安全な隔離と使いやすさはぶつかりやすい

webview を隔離するのは正しい。
でも、ユーザー体験のためにキーボード入力を中継する。
この「親切な機能」が、攻撃者にとっては便利な足場になる。

2. “ユーザー操作っぽいもの” は危ない

セキュリティでは、​本物のユーザーが操作したのか、それともスクリプトがそれっぽく見せたのか が超重要です。
ここを雑に扱うと、UI 自動化や入力偽装にすり抜けられます。

image_0014.png

3. ひとつの脆弱性より、複数の小さな設計の隙が重なるほうが怖い

この攻撃は、1個の穴だけで成立しているわけではなさそうです。
webview、postMessage、キーボード中継、通知、workspace extension、publisher trust、CSP……
それぞれは「妥当な仕組み」なのに、組み合わせると危険になる。ここが本当に面白くて、同時に嫌なところです。

VSCode側がよくやっていた点

記事では「VSCodeが良くやっていた点」も触れています。
これは重要です。バグ報告を雑に煽るのではなく、​何が守りになっていて、何が足りなかったのか を見るのが大切だからです。

少なくとも、webview を別 origin に分けるという基本設計は正しい。
また、publisher trust の仕組みも、安易に extension を入れられないようにする防波堤としては意味があります。

image_0015.png

つまり、VSCodeは何もしていなかったわけではない。
むしろ、ちゃんと防御しようとしていた。それでもなお、​​「ユーザー利便性のための例外処理」​ に脆さが入り込んだ、という見方が自然だと思います。

個人的な感想

個人的には、この話はかなり「現代的な脆弱性」だと思います。
昔ながらの、単純に入力を食わせれば落ちる、みたいな話ではありません。
むしろ、​大きくて便利なWebアプリが、いろんな例外や互換性のために少しずつ複雑になり、その隙間を突かれる というタイプです。

これ、ソフトウェア開発の現場ではほんとうによく起きます。
「これはユーザーのため」「これはUXのため」「これは互換性のため」と積み上げた結果、境界がじわじわ曖昧になる。
そして攻撃者は、その曖昧さを見つけるのがうまい。

image_0016.png

だからこそ、こういう記事は単なる怖い話ではなく、​設計レビューの教材 として読む価値があると思います。

まとめ

この記事が伝えているのは、
ブラウザ上で動くVSCodeのwebviewにある入力中継の仕組みを悪用すると、GitHub token を盗める可能性がある、ということです。

しかも面白いのは、攻撃が「いかにも攻撃」ではなく、

image_0017.png

といった、​全部ふつうの機能の組み合わせ で成立している点です。

つまり、「変なコードを一発で実行する」のではなく、
正規機能の信頼を少しずつ踏み台にする 攻撃。
これは本当に厄介だし、セキュリティの面白さでもあります。


参考: 1-Click GitHub Token Stealing via a VSCode Bug

同じ著者の記事