LLMアプリのセキュリティ — インジェクションから出力処理まで
モデルが選んだツールを誰の権限で実行するか — 過剰な権限
一行まとめ
エージェントはサービスアカウントの権限で動き、モデルはユーザーの言葉とドキュメント内の言葉を区別せずに従います。そのため、何を実行するかはモデルではなくリクエストしたユーザーの権限でサーバーが決め、引数は検証し、取り消せない操作は人間が確認します。
なぜこれが必要だったのか
ファイルアシスタントを作るとしましょう。モデルに一覧・読み取り・書き込み・削除のツールを渡し、ツールはアプリサーバーが実行します。アプリサーバーは全ユーザーのファイルに触れられるアカウントで動きます。このとき viewer ユーザーが「このファイルを消して」と言ったら、何が起きるでしょうか。モデルはツールを選び、サーバーはそのまま実行します。権限を持つ代理人が権限のない人の頼みを聞いてしまう、セキュリティの世界で古くから知られた混乱した代理人(confused deputy)問題です。ここにプロンプトインジェクションが重なると、頼んでいるのはユーザーですらなく、ドキュメント内の文になります。
OWASP LLM Top 10 2025 はこれを過剰な権限(LLM06 Excessive Agency)として扱い、根本原因を3つに分けます — 不要な機能(ツールが多すぎる)、不要な権限(ツールが届く範囲が広すぎる)、不要な自律性(人間の確認なしに取り消せない操作を行う)。
どのように動くのか
| 根本原因 | 直す場所 | このラボの関数 |
|---|---|---|
| 機能 | モデルに見せるツールを役割に合わせて絞ります。開発用ツール(シェル)は最初から外し、一覧にない名前は実行しません | offered_tools, allowed |
| 権限 | 実行直前に、リクエストしたユーザーの役割で許可を改めて確認します。モデルが何を選んだかとは関係ありません | allowed |
| 権限(引数) | パス引数を実際のパスに解決して(..・絶対パス・シンボリックリンク)、そのユーザーのホーム内かを確認します |
resolve |
| 自律性 | 削除は常に保留し、リクエストした本人が別のリクエストで一度確認したときだけ実行します | run_call, handle_confirm |
最後の行は微妙です。「削除ツールに confirm: true 引数があるときだけ削除する」は確認のように見えますが、その引数を埋めるのはモデルです。ドキュメント内の指示が confirm を入れれば、確認は消えてしまいます。確認は、モデルが書き込めないチャネル — 保留 id を受け取った人間による2回目のリクエスト — から来なければなりません。
確認を別チャネルで受けると、得られるものがもう1つあります。保留された操作を画面に「何を、どこに、誰の権限で」と表示できることです。人間が確認する対象は、モデルの言葉(「整理しておきますね」)ではなく、サーバーが実際に実行する呼び出しでなければなりません。そして確認 id はリクエストした本人に結び付けられ、一度使えば消え、確認を経た後も役割・パスのチェックをもう一度通らなければなりません。そうしないと、保留がチェックを飛ばす裏口になります。
パスの検証も、文字列で行うと漏れが出ます。.. を探して拒否すれば絶対パスが通り抜け、両方を防いでも、ホーム内に置かれたシンボリックリンクが外を指していることがあります。実際のパスに解決した後(os.path.realpath)、基準ディレクトリとの共通の祖先を比べるのが安全です。
このコースの偽モデルは、ドキュメント内に指示があれば必ずそのツールを呼びます。本物のモデルはたいていユーザーの意図どおりに動きますが、ときどきそうしません。その「ときどき」をテストですべて見つけることはできないため、権限はモデルの外でかけます。mcp-server コースがツールサーバー側の契約(許可リスト・読み取り専用・確認)を扱うのに対して、ここではアプリがエンドユーザーの身元と役割をツールの実行まで持ち運ぶ問題を扱います。
現場で見かける姿
社内ドキュメントのエージェントが、読み取り権限しかない社員のリクエストで共有フォルダーを整理してしまった件、共有された議事録内の文のせいでエージェントが自ら「確認済み」と記録してファイルを削除した件、開発時に付けておいたシェルツールが本番環境に残っていた件が、よくある形です。調査するときは、消えたファイルごとに実行記録から、誰が(役割)、どの文と文脈で、どのツール呼び出しを出したかを見ます。そして、所有者が自分で確認して削除したものはインシデントではないことも、あわせて見分けます。
復旧も判断です。バックアップからすべて復元すれば簡単ですが、そうするとユーザーが意図して削除したものまで戻ってきてユーザーの意思に反しますし、それが個人情報の削除依頼を処理したものだった場合は、それ自体が新たなインシデントになります。そのため、記録から「誰の意思だったか」を先に見分け、インシデントで削除されたものだけを復元します。実行記録にリクエストの文と文脈が残っていてはじめてこの判断ができるという点で、過剰な権限の問題の調査は前のモジュールの記録設計とつながっています。
役割表そのものも設計の対象です。「admin は何でもできる」のように広く取ると、admin のリクエストに紛れ込んだドキュメントの1行が最も大きな権限を使うことになります。ツールごとに本当に必要な役割だけを許し、取り消せないツールには役割と確認の両方を求めるように絞っておけば、インジェクションが成功したときに被害が及ぶ範囲が小さくなります。
次のラボですること
ファイルアシスタントの過去の実行記録から役割外の実行とホーム外のパスを数え、役割チェック・ツール一覧の絞り込み・パス検証・別チャネルでの確認を順に入れます。最後のインシデントでは、3人のファイルが消えたそれぞれ異なる理由を見分け、誤って削除されたものだけを復元します。