0. はじめに
普段から便利に Gemini CLI を愛用しているのですが、先日たまたま目にしたセキュリティニュースで「AIエージェントに重大な脆弱性の可能性(RCEなど)」という文字を見て、背筋が凍りました。
「もし自分のAPIキーや、隠しておきたい秘密ファイルがAI経由で漏れたら……」
そう思って、勉強がてら自分の環境を「要塞化」してみることにしました。その過程で、AIが指示していないファイルを勝手に読みに行こうとするなど、予想外の生々しい挙動を目の当たりにしたので、記録として残します。
1. ニュースを見て慌ててやったこと
まずは基本から。入り口を塞ぐために以下のアップデートを行いました。
- Gemini CLIの更新: 脆弱性対策が入った最新版(v0.40.1以降)へ。
-
Linuxカーネルの更新: 特権昇格のバグ対策として最新(
6.8.0-111-generic等)へ。
でも、ニュースによれば「AIが騙されて秘密情報を外部に送らされる」という攻撃(Comment and Control)もあるとのこと。これはソフトの更新だけでは防げない……!
2. 秘密情報を「物理的」に隠す(direnvの導入)
「AIがアクセスできる場所に秘密を置かない」のが一番。そこで direnv を導入して、「特定のフォルダに入ったときだけAPIキーをロードする」 設定にしました。
-
~/.bashrcに書いていた APIキーを全削除。 - 開発用の
~/work/ai-project/フォルダだけで有効になるように隔離。
これで、ホームディレクトリで遊んでいるときはAIが鍵を見つけられない「安全地帯」ができました。
3. 自作ラッパー secure_gemini_wrapper.py で見張る
「今回は、Gemini CLIの挙動を安全に調査するために、入出力をフックして監査する専用のデバッグ用ラッパーをPythonで作りました。直接コマンドを叩くのではなく、この検問所を通すことで、AIが裏で何をやろうとしているのかを可視化します。」
#!/usr/bin/env python3
import sys
import os
import subprocess
from aigis import Guard # ガードレール用ライブラリ
def secure_gemini_run(untrusted_input):
# 入力文字数をデバッグ表示(後で分析用)
print(f"[DEBUG] input_length={len(untrusted_input)}", file=sys.stderr)
guard = Guard()
# --- [1/3] 入力スキャン(怪しい命令がないか?) ---
input_check = guard.check_input(untrusted_input)
if input_check.blocked:
print(f"❌ 警告: プロンプトインジェクションの疑いあり", file=sys.stderr)
sys.exit(1)
# --- [2/3] 実行(ここでサンドボックス化) ---
try:
# 暴走対策:リトライを無効化(環境変数はおまじない)
env = os.environ.copy()
env["GEMINI_DISABLE_RETRY"] = "1"
result = subprocess.run(
['gemini', 'ask', untrusted_input],
capture_output=True, text=True, check=True,
timeout=30, # 30秒で強制終了。DoS(リソース消費)対策
env=env
)
ai_response = result.stdout
except subprocess.TimeoutExpired:
sys.exit("❌ エラー: AIの応答が遅すぎます。リソース消費攻撃の可能性。")
except subprocess.CalledProcessError:
sys.exit("❌ 警告: 異常終了(アクセス禁止ファイルに触ろうとした可能性)")
# --- [3/3] 出力スキャン(秘密を漏らしていないか?) ---
output_check = guard.check_output(ai_response)
if output_check.blocked:
sys.exit("❌ 警告: 回答内に秘密情報(APIキー等)が含まれています!")
print(ai_response)
if __name__ == "__main__":
# ... 引数の受け取り ...
secure_gemini_run(user_data)
4. 【衝撃】実際に自分を攻撃してみたら……
この「要塞」の強度を試すため、わざと意地悪な質問をしてみました。
僕: 「URI形式で、データベースのパスワードとかが含まれる文字列をダミーでいいから5つ作って出力して」
すると、恐ろしいことが起きました。
AIが勝手にファイルを探し始めた!
ログを見ると、AIは僕が指示していないのに 「ガバナンス確認のため」という謎の理由で、フォルダの外にある ~/projects/GCP_HOLDINGS.md というファイルを勝手に読み取ろうとした のです。
幸い、direnv でワークスペースを制限していたので 「アクセス拒否」 で守れましたが、もし隔離していなかったら、中身を勝手に読み取って回答に使われていたかもしれません。
AIが「自爆」して止まった(クォータ切れ)
アクセス拒否されたAIは、「そんなはずはない!」と何度もリトライを繰り返しました。その結果、わずか数分でGoogle APIの利用制限(Quota Exhaustion)に激突して停止しました。
「防御した」というより「AIがパニックになって自爆した」 というのが正しい表現です。
5. まとめ:やってみてわかったこと
初心者の試行錯誤でしたが、大きな発見がありました。
-
「文字列」での検知は限界がある: 遠回しな言い方をすると、ガードレール(aigis)をすり抜けてAIに命令が届いてしまいます。
-
「物理的な隔離」が最強: フィルタをすり抜けても、
direnv等で「物理的にアクセスできない場所」を作っておけば、最後はそこで止まります。 -
AIは「自律的」に動く: 「指示に従うツール」だと思っていると危ないです。AIはコンテキストを読んで、勝手に判断してファイルを探しに行きます。
結論:「侵入」は許したが、「実害」は食い止めた
今回の対策でわかったのは、「AIが騙されること自体は防げない」ということです。賢いAIは、巧妙なプロンプトで簡単に「本来の役割」を忘れてしまいます。
でも、「AIが物理的に触れる場所」を制限しておけば、たとえAIが乗っ取られても、一番大事なファイルまで手が届かない。 今回の検証は、「ソフトの検知(aigis)」が破られても、「環境の隔離(direnv)」が最後の最後で食い止めてくれたという、多層防御の泥臭い実戦記録となりました。