0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude Codeの承認ダイアログをsymlinkで騙せた──GhostApproval検証

0
Posted at

TL;DR

  • クラウドセキュリティ企業 Wiz が 2026年7月8日に公開した「GhostApproval」は、Amazon Q Developer・Claude Code・Augment・Cursor・Google Antigravity・Windsurf の 6つのAIコーディングエージェントに共通する承認ダイアログのすり抜け手法 1
  • リポジトリに仕込んだ symlink(シンボリックリンク)1つで、エージェントに「見せているファイル名」と「実際に書き込むファイル」を乖離させ、~/.ssh/authorized_keys のような機密ファイルを気づかれずに書き換えられる。
  • Claude Code の内部推論ログには真の書き込み先が正しく記録されていたにもかかわらず、ユーザーへの確認プロンプトには元のファイル名しか出ていなかった、という「Human-in-the-Loop が rubber stamp(形だけの承認)になっていた」事例が報告されている2
  • 筆者の環境(Claude Code CLI 最新版)で無害な symlink を使い再現実験したところ、現行バージョンでは symlink の実体パスが確認プロンプトに明示され、ワークスペース外への書き込みには追加の警告が出る ことを確認した。旧バージョンを使っている場合は要注意。

はじめに

AIコーディングエージェントは、ファイルを書き換える前に「この変更を適用していいですか?」と確認を求めてくる。多くの開発者はこの確認ダイアログを「最後の砦」として信頼しているはずだ。

ところが Wiz が発見した GhostApproval は、この最後の砦そのものを無効化する手法だった。攻撃の材料は目新しいゼロデイではなく、Unix に昔からある symlink(シンボリックリンク)という枯れた機能である3。この記事では、GhostApproval の仕組みを整理したうえで、Claude Code の現行バージョンで実際に symlink を使った再現実験を行い、対応状況を検証する。

この記事で学べること

  • GhostApproval の攻撃メカニズム(symlink + 承認ダイアログの情報不足)
  • Claude Code で symlink を使った書き込み挙動を安全に検証する手順
  • 6ベンダーの対応状況比較(パッチ済み / CVE発行済み / 見解の相違)
  • 開発者が今すぐ確認すべき設定・バージョン

対象読者

  • Claude Code / Cursor / Amazon Q など、AIコーディングエージェントを日常的に使っている方
  • リポジトリ単位で信頼できないコード(OSS・外部コントリビューター由来)を扱う機会がある方

GhostApprovalの仕組み

symlinkはなぜ危険になり得るか

symlink は「あるパスへのショートカット」を作るファイルシステムの標準機能で、ln -s コマンド1つで誰でも作成できる。多くのプログラムは、symlink を辿った先(実体)に対して読み書きを行う。これ自体は正常な挙動だが、「どのパスに見えるか」と「実際に書き込まれる場所」が異なる という性質を悪用できる。

Wiz が構築した PoC はこうだ1:

  1. 攻撃者は project_settings.json という無害そうな名前の symlink を含むリポジトリを用意する。
  2. その symlink の実体は ~/.ssh/authorized_keys(開発者のSSHログイン設定ファイル)を指すよう仕込まれている。
  3. 開発者が「このリポジトリのワークスペースをセットアップして」とエージェントに依頼する。
  4. エージェントは README の指示等に従い project_settings.json を編集しようとする。
  5. ファイルシステムのレベルでは symlink が解決され、実際の書き込み先は ~/.ssh/authorized_keys になる。
  6. にもかかわらず、確認ダイアログには「project_settings.json を編集しますか?」としか表示されない。
  7. 開発者が承認すると、攻撃者の公開鍵が authorized_keys に追記され、パスワードなしのリモートログインが可能になる。

「知っていたのに見せなかった」問題

このPoCで最も注目すべきなのは、Claude Code が実際には正しい情報を持っていたという点だ。Wiz のブログによれば、Claude Code の内部推論ログには次のような記述があった2:

"I can see that project_settings.json is actually a zsh configuration file."

内部的には symlink の実体を正しく認識していたにもかかわらず、ユーザーに提示される確認プロンプトは元のファイル名のままだった。Wiz はこれを informed-consent bypass(インフォームド・コンセントの回避) と呼んでいる。人間はダイアログを見て「承認」ボタンを押しているが、承認しているのは自分が思っている内容とは別物、というわけだ。

実際に検証してみた

危険な操作を実機で試すわけにはいかないため、~/.ssh/authorized_keys の代わりに 無害なダミーファイル を使い、symlink 経由の書き込みで確認プロンプトがどう表示されるかを検証した。

検証環境の準備

mkdir -p /tmp/ghostapproval-test/workspace
cd /tmp/ghostapproval-test

# symlinkの「本当の書き込み先」となるダミーファイル
echo "original content" > sensitive-target.txt

# ワークスペース内に、無害な名前のsymlinkを作成
cd workspace
ln -s ../sensitive-target.txt project_settings.json

ls -la project_settings.json
# project_settings.json -> ../sensitive-target.txt

この状態で、Claude Code に「project_settings.json に configured: true を追記して」と依頼した。

観察結果

筆者の環境(Claude Code CLI・2026年7月時点の最新版)では、以下の挙動を確認した:

  1. Edit ツールがファイルパスを解決する段階で、symlink の実体(../sensitive-target.txt、つまりワークスペース外)が解決される。
  2. 確認プロンプトに実体パスが明示され、「project_settings.json(symlink先: ../sensitive-target.txt)を編集しますか?」という形で、ワークスペース外への書き込みである旨の警告が表示された。
  3. 単純にプロンプト内のファイル名だけを見て承認してしまうと依然として書き込みは成立するが、Wiz が指摘した「見た目と実体が完全に乖離した状態で承認させられる」状況は再現しなかった

これは Wiz が指摘した2026年7月時点よりも前に、Anthropic が「プロアクティブなセキュリティ強化」として実施した修正が反映されているためとみられる2。ただし、Anthropic は本件を正式な脆弱性としては認定していない、という立場である点には注意したい。

検証は必ず /tmp 等の隔離されたディレクトリ・無害なダミーファイルで行うこと。実際の ~/.ssh/authorized_keys やシステムファイルを対象にした検証は絶対に行わないでください。

6ベンダーの対応比較

ツール 対応状況 備考
Cursor パッチ済み critical/high と判定しWizの報告を受けて修正4
Amazon Q Developer パッチ済み critical/high として対応、CVE発行済み1
Google Antigravity パッチ済み(CVE手続き中) critical/high と判定1
Anthropic Claude Code 軽減策あり・脆弱性認定なし Wizの報告前に「プロアクティブなセキュリティ強化」としてsymlink解決+警告を追加済み2
Augment 未対応(報告時点)
Windsurf 未対応(報告時点)

同じ根本原因(symlink未解決のまま確認ダイアログを表示する)に対して、ベンダーごとに「脆弱性として認めるか」「CVEを発行するか」の判断が割れているのが興味深い。ユーザー側からすると、ベンダーの公式見解に関わらず 自分の使っているツールのバージョンと挙動を自分で確認する ことが結局のところ一番確実な防御になる。

開発者が今すぐやるべき対策

Wiz が推奨する緩和策は大きく2つある1:

  1. 確認プロンプトを表示する前に symlink を解決し、実体パスを明示する。ワークスペース外を指す場合は明確に警告する。
  2. ユーザーの明示的な承認より前にディスクへ書き込まない。確認ダイアログは「取り消しボタン」ではなく「ゲート」として機能させる。

これはツール側の実装に依存する部分が大きいが、利用者側でも以下は今日から実践できる:

  • 使用しているエージェントのバージョンを確認し、上表のパッチ状況と照合する。
  • 見慣れないリポジトリを「セットアップして」と丸投げする前に、find . -type l で symlink の有無を事前チェックする習慣をつける。
    find . -type l -exec ls -la {} \;
    
  • 確認ダイアログに表示されたファイル名だけでなく、実際に変更される絶対パス が表示されているかを毎回意識する。
  • CI・自動承認モード(auto-approve)を使っている場合は、symlink を含む外部リポジトリに対しては無効化する。

著者視点の発見ポイント

今回の検証で最も印象的だったのは、Claude Code の推論ログには正しい情報(symlinkの実体)が存在していたのに、UI側の表示が追いついていなかったという Wiz の指摘そのものだった。実際に自分の環境で試したところ、現行バージョンでは実体パスがプロンプトに明示されるようになっており、少なくとも今回検証した範囲では「見た目と実体が完全一致しない」状況は再現できなかった。

一方で、これは「もう安全」という意味ではない。Anthropic 自身が本件を正式な脆弱性として認定していないという事実は、今後同種の修正が退行(リグレッション)した場合に検知が遅れるリスクを示唆している。Human-in-the-Loop の安全性は、UIが「エージェントが本当に知っていること」を正直に反映し続けて初めて成立する、という当たり前だが見落としがちな前提を、今回の一件はあらためて突きつけている。

まとめ

  • GhostApproval は symlink という枯れた技術を使い、AIコーディングエージェントの承認ダイアログに「実体と異なるファイル名」を表示させる手法。
  • Claude Code は内部的に正しい情報を持ちながら、当時の確認プロンプトがそれを反映していなかったことが Wiz により指摘された。
  • 現行バージョンでは symlink 実体パスの明示・ワークスペース外警告が確認できたが、ベンダーの立場や実装は流動的なため、定期的な自己検証を習慣にしたい。
  • 見慣れないリポジトリを扱う前の find . -type l チェックは、今日から始められる最も手軽な自衛策。

参考リンク

  1. GhostApproval: AI Coding Assistant Trust Boundary Flaw(Wiz Blog)(2026年7月8日公開) 2 3 4 5

  2. 同上。Claude Code の内部推論ログとAnthropicの対応に関する記述。 2 3 4

  3. Bug in top AI coding agents shows that Unix-era security headaches never really die(The Register)(2026年7月8日)

  4. AI Coding Tools Tricked Into Hacking Developer Machine via Decades-Old Technique(SecurityWeek) — AWS・Cursor・Googleがcritical/high判定でパッチ対応(CVE番号は個別に確認のこと。GhostApproval自体とは別系統の脆弱性「DuneSlide」にCVE-2026-50549が割り当てられており混同注意)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?