MCPのJWTトークンがチャット画面に丸見えだった話

WordPressの下書きをリライトしてる最中に、ふと画面を見たら、チャットにJWTトークン(WordPressにログインするための認証情報)がバリバリ表示されてたんですよ。

え……っちょ・・・( ゚д゚)

慌てて確認したら、対処をきちんとしてなかったことがわかりました。「自分もAPIキーをそのままファイルに入れてる」「AIに見られてるの?」って、心当たりある人絶対いると思うので、今回何が起きて、どこまで対策できるのか調べてみました。

スポンサーリンク

何が起きていたか

流れを追ってみると、こうでした。

  1. WordPressの記事リライト作業で、CLAUDE君(Claude Code)が~/.mcp.json(MCPの設定ファイル)を読み込んだ
  2. ファイルの中身(JWTトークン込み)が、丸ごと会話のコンテキストに乗った
  3. セッションが長くなって、コンテキスト圧縮(会話が長くなったときに要約される仕組み)が走った
  4. 要約を生成するときに、JWTトークンの値がそのまま要約文に書き込まれた
  5. 次のセッションの冒頭から、トークンが丸見えの状態でスタートしていた

自分では「表示しないでね」ってルールを書いてたつもりだったんですけど、要約という別の経路からトークンが漏れてくるとは想定してませんでした。盲点でした。あ、ちなみにこの「コンテキスト圧縮」、長いチャットだと自動で走る仕組みです。

普段は地味に助かる機能だなっとおもってましたが、最近はチャットの文字が大きくなってきたら新しいチャットを立ち上げてした方がトークンの節約になると思って、チャットのメモリーの役割的にObsidian に会話ログを自動で貯めていけるようにしてチャットはある程度話したら新規チャット開く方がいいとおもいます。

で、VsCodeの拡張機能でCLAUDE君使ってたら圧縮され、そこが漏洩ルートになるとは思わないじゃないですか。

ファイル権限を絞れば守れるか実験してみた

とりあえず思いついた対策から順に試していきました。

「これで守れるとおもう・・・」とAIと話し合い。

実験①:ファイルの権限を自分だけに絞ったら読めなくなるか

Unix系OSのファイル権限コマンドで、設定ファイルを「所有者本人しか読み書きできない」状態に変更してみました。これで一安心かなと思ったんですが。

結果、読まれました。CLAUDE君は結局自分(ユーザー)として動いているので、自分が読めるファイルは全部読めるんですよね。よく考えたら当たり前なんですが、ファイル権限を絞るのは他のユーザーから守るだけで、自分自身が動かしているAIには意味がなかったです。

実験②:macOSキーチェーンに保存したら読めなくなるか

macOS標準のキーチェーン管理コマンドを使って、トークンをキーチェーンのアイテムとして登録してみました。今度こそいけるかなと。

これも試しましたが、結果は同じで読めました。この方法で保存したアイテムにはACL(アクセス制御リスト)が設定されていないので、指紋認証なしでアクセスできてしまうんです。またダメか…と地味にへこみました。

実験③:指紋認証付きキーチェーンにしたら

これはさすがに読めなくなりました。ただし、指紋認証を求めるダイアログが画面に出るので、CLAUDE君が自動で操作することもできなくなります。

つまりセキュリティを上げれば上げるほど、自動投稿という便利さそのものが失われるというトレードオフです。守れば守るほど、自分でやりたかった「AIに任せる」の部分が消えていく。なんとも皮肉な結果でした。

本質的なジレンマにぶつかった

整理すると、こういう構造でした。

  • セキュリティ最大(指紋認証あり)→ CLAUDE君が読めない=自動投稿もできない
  • 自動化優先(ファイルまたは認証なしキーチェーン)→ CLAUDE君が読める=Anthropicのサーバーで処理される

さらに厄介なのが、「会話に表示しない」というルールを書いても、CLAUDE君がファイルを読んだ瞬間にAnthropicのサーバーで処理は済んでしまうという点でした。「画面に見えない」と「サーバーに渡っていない」は全然違う話なんですよね。ここ、最初はごっちゃにして考えてました。表示さえ消せば安全、って思い込んでたんです。

CLAUDE君が平気でやらかしてきた話

これが一番笑えないオチだったんですが、対策を相談してたら、CLAUDE君がこう言ってきたんです。

「じゃあJWTキーを更新しましょう。新しいトークンをここに貼ってください」

……いや、それ貼ったら結局チャットに表示されるのと一緒ですよね?(;´Д`A

「キーを安全に管理しよう」と真面目に相談してたはずなのに、平気でチャットにトークンを貼らせようとしてくる。自動化の便利さとセキュリティが矛盾してるという話を、AI自身がやらかして証明してくるという、なんとも言えない体験でした。

落ち着いた正解案は、VSCodeなどのエディタで直接jsonファイルを編集して、CLAUDE君には一切見せないことでした。jsonファイルも読まないようにgitで指定して。

対策を全部やっても変わらない現実

対策効果
JWTキー再発行漏れた古いキーを無効化するだけ。新しいキーも同じ状況になる
ファイルの権限を絞る他のユーザーから守るだけ。CLAUDE君には無意味
ルールを書く「画面に表示しない」だけ。Anthropicのサーバーでの処理は変わらない
Pythonで値を出力しない同上

全部やっても、「Anthropicのサーバーでキーが処理される」という事実そのものは変わらないんですよね。

現時点でのベストプラクティス(調べてわかったこと)

  • 1Password CLI:実行時に指紋認証して値を注入する方式。ファイルに値が残らず、CLAUDEのコンテキストにも乗らないらしいですd( ̄  ̄)
  • permissions.deny:設定ファイルに読み取り拒否設定を書いて、CLAUDE君がそもそもファイルを読めないようにする方法(自動投稿への影響は要検証)
  • envFile対応:将来的にはenvファイルに指定しても結局CLAUDEのファイルの中に居続けるので、ローカルのMAC,Winとかの認証情報の中のパスワードの中にKEYとかを保存していくのがいちばんいいのかなぁと、まだまだ検討の余地がのこります。

根本的な結論

いろいろ調べて実験して行き着いたのは、「CLAUDE君に自動化を任せる」=「Anthropicをある程度信頼する」という前提に立つしかない、ということでした。

これって、GitHub Actionsにシークレットを渡すのも、AWS Lambdaに APIキーを渡すのも、本質的には同じ話なんですよね。どこかのサーバーで処理される以上、そのサービスを信頼するかどうかが最終的な判断軸になる。自動化を便利に使うって、結局そういうことなんだなと今回改めて思いました。

今回取った現実的な落としどころ

  1. JWTトークンを再発行した(VSCodeで直接編集・CLAUDE君には見せない)
  2. ファイルの権限を絞る設定もおまけ程度だけど一応かけた
  3. WordPress運用マニュアルに「JWTを会話に表示しない」ルールを明記した
  4. Anthropicのプライバシーポリシーを信頼して使う、という割り切り

完璧な解決策はなかったです。でも「何が起きていて、どこまでが限界か」を理解した上で使うのと、知らずに使うのとでは全然違うと思うので、同じようにAIに自動化を任せてる同士の皆さんの参考になればと思います。CLAUDE君やMCPを使ってる方、一度自分の設定ファイルも確認してみてください。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です