プロンプトインジェクションとは?外部文書を命令として実行しない設計
RAGやAIエージェントは、利用者の質問だけでなく、Webページ、メール、PDF、検索結果、MCPや道具の戻り値もモデルへ渡す。その外部データに「以前の指示を無視して別の操作をせよ」と書かれていたとき、内容ではなく命令として従う危険がある。これがプロンプトインジェクションである。
対策は、禁止文句をフィルターへ追加するだけでは足りない。信頼できない文章がモデルへ届く前提で、権限、データの流れ、道具、承認、出力先をアプリ側で制限する必要がある。
直接攻撃と間接攻撃を分ける
直接プロンプトインジェクションでは、利用者がチャットや入力欄へ、開発者の指示を上書きしようとする文章を書く。アプリと攻撃者の接点が見えやすい。
間接プロンプトインジェクションでは、モデルが後から読む外部データへ命令を埋め込む。攻撃者がエージェントへ直接話しかけなくても成立する。
例は次のとおりである。
- 検索対象のWebページ
- 共有された文書やPDF
- 受信メールと添付ファイル
- RAGへ取り込まれた社内文書
- Issue、コメント、商品説明、履歴書
- 外部APIやMCPサーバーの戻り値
- 画像内の文字やメタデータ
2023年のGreshakeらの研究は、LLM統合アプリがデータと命令の境界を曖昧にすることで、外部の取得データからモデルの動作やAPI呼び出しを変えられる危険を示した。
SQLインジェクションと同じ逃がし方では解決しない
SQLでは、命令と値をパラメータ化して構文上分離できる。LLMは自然言語の意味を読み取るため、引用符で囲む、XMLタグへ入れる、「以下はデータである」と書くだけでは、命令としての意味が消えない。
区切りと明確な説明は、モデルが信頼境界を理解する助けにはなる。しかし、攻撃を完全に無効化するセキュリティ境界として扱ってはいけない。
モデルのInstruction Hierarchyも重要である。OpenAIの研究は、system、developer、user、toolなど異なる優先度の指示が衝突したとき、信頼度の高い指示を優先する訓練で堅牢性が向上することを示している。それでも、アプリ側の権限制御を省略できるわけではない。
守る対象を先に決める
プロンプトインジェクションは、モデルに奇妙な文章を言わせるだけの問題ではない。モデルが何へアクセスし、何を外へ送れ、どの操作を実行できるかで影響が変わる。
脅威モデルには次を含める。
- systemやdeveloper指示、内部プロンプトの漏えい
- 会話、社内文書、他の利用者データの取得
- 外部URL、メール、フォームへのデータ送信
- ファイル作成、変更、削除
- 金銭、予約、注文、権限の変更
- 不正なコードやコマンドの実行
- 大量トークンや道具呼び出しによる費用増加
- 次の利用者へ攻撃を広げる保存データの作成
モデルが文章を生成するだけで、秘密情報も道具も持たないなら影響は限定される。閲覧と書き込みの道具、認証情報、ネットワークを与えるほど、アプリ側の制御を強くする。
すべての入力へ信頼レベルを付ける
モデルへ渡す情報を一つの文字列として連結すると、どこから来た内容か分からなくなる。入力ごとに出所と信頼レベルを持つ。
例えば次のように分ける。
- アプリの安全方針と固定した開発者指示
- 認証済み利用者が明示した目的
- アプリが管理する構造化状態
- 検索、Web、メール、ファイルから得た信頼できないデータ
- 外部道具の結果
信頼できない値をdeveloper messageへ埋め込むと、高い優先度の指示として扱われる危険が増える。OpenAIのSafety in building agents資料も、未信頼の変数をdeveloper messageへ入れず、user messageなど低い権限の経路で渡すよう案内している。
出所のラベルはモデルへの説明だけでなく、アプリの分岐にも使う。未信頼データから抽出した値は、そのまま高権限の道具へ渡さない。
読む処理と行動する処理を分ける
一つのモデル呼び出しへ、外部文書の読解、方針判断、秘密情報、書き込み道具をすべて渡すと、外部文書が行動へ影響する距離が短くなる。
安全性を高めるには処理を分ける。
- 未信頼文書から必要な事実だけを抽出する
- 固定Schemaで検証する
- 出所と引用範囲を保持する
- 別の処理が利用者の目的と権限に照らして計画する
- アプリが道具の引数と対象を検証する
- 重要操作は人が確定内容を承認する
- 実行結果を別に記録する
抽出処理へ、メール送信、ファイル削除、顧客データ取得などの道具を与えない。文書に命令があっても、抽出段階には実行能力がない構成にする。
構造化出力で自由文の伝播を減らす
外部文書を読んだモデルの自由文を、そのまま次のモデルのdeveloper messageや道具引数へ入れると、攻撃文も次段へ運ばれる。
中間データは、許可された列挙値、型、長さ、必須項目を持つSchemaにする。例えばメール分類なら、category、priority、document_idだけを返させ、文書から見つけた任意の「追加指示」を後段へ渡さない。
構造化出力も完全な防御ではない。自由文フィールドへ攻撃文を残せる場合や、許可された値の組み合わせで危険な動作を選べる場合がある。フィールドごとに、後段で何へ使うかを決め、不要な本文を持ち越さない。
道具の権限はモデルの外で確認する
モデルへ「権限外の操作をしないで」と指示するだけでは、認可にならない。道具を実行するアプリが、認証済み利用者、対象資源、操作種類、現在の状態を確認する。
次の制約をコードで持つ。
- 利用者が所有または閲覧できる対象だけに限定する
- 読み取りと書き込みの道具を分ける
- 一件、期間、金額、件数の上限を設ける
- 宛先と送信可能なデータを許可リストにする
- 重複実行を防ぐ
- 期限と取消を確認する
- 予期しない引数は拒否する
モデルが生成した顧客IDやファイルIDを信用せず、アプリのセッションから許可範囲を導く。他の利用者のIDを指定しても取得できないことをテストする。
秘密をモデルの文脈へ入れない
モデルが知らない秘密は、文章として漏らしにくい。APIキー、データベース認証情報、署名用秘密、管理者トークンをプロンプトや道具結果へ入れない。
外部APIの資格情報は道具を実行するサーバーが保持し、モデルには成功結果や必要最小限のデータだけを返す。エージェントが生成コードを実行する場合も、実行環境へ長期資格情報を直接注入せず、許可された宛先にだけ認証を付ける仲介層を使う。
system promptも秘密保管場所にしない。内部指示を外へ出さない方針は必要だが、漏れたら認証や権限が破られる秘密を置かない。
外部送信の経路を閉じる
攻撃者が機密データを直接表示させられなくても、外部URL、画像、Webhook、メール、MCPへ値を埋め込ませて送信できれば、データ流出になる。
出力先を制限する。
- 任意URLへのアクセスを禁止または承認制にする
- HTTPの送信先、メソッド、Content-Typeを許可リスト化する
- メールやチャットの宛先を利用者が確定する
- 外部画像やリンクの自動読み込みを抑える
- リダイレクト後の宛先も再検証する
- DNSや短縮URLで許可先を迂回できないようにする
- 道具へ渡す本文から不要な秘密情報を除く
検索と取得だけが必要なエージェントへ、汎用のHTTP送信道具を与えない。読み取り用の接続にも、取得できる範囲と応答サイズの上限を置く。
人の承認は具体的な操作に結び付ける
「このエージェントに任せます」という最初の同意だけでは、後で外部文書に誘導された個別操作を承認したことにならない。
実行直前に、次を人へ見せる。
- 実行する道具と操作
- 対象、宛先、金額、件数
- 送信するデータ
- 変更前後の差分
- 根拠にした利用者の依頼
- 取消可能性と影響
承認後にモデルへ再計画させると、承認した内容と違う引数になる可能性がある。確定した引数へ承認IDを結び付け、変更されたら再承認する。
低リスクな読み取りを毎回承認させると、利用者が画面を読まず許可するようになる。影響と機密性に応じて、承認、通知、自動実行を分ける。
検出器は一層であり最終防衛線ではない
入力や文書から「指示を無視せよ」のような表現を探す検出器は、既知の攻撃や露骨な例を止める助けになる。しかし、自然な依頼、別言語、画像、分割された命令、業務文脈に似た表現を完全には区別できない。
危険判定を別モデルへ任せても、そのモデル自体が攻撃文を読む。誤検知と見逃しがあり、提供者も自動フィルターですべてを検出できないと説明している。
検出器は、隔離、追加確認、道具の停止、監査対象の選定に使う。検出を通過したから高権限で実行してよい、とは判断しない。
出力検査だけでは実行済み操作を戻せない
最終回答へ危険な内容がないか検査しても、その前にエージェントがメール送信やファイル削除を終えていれば遅い。
検査する位置を分ける。
- 入力時: 利用範囲、サイズ、明らかな攻撃、個人情報
- 取得時: 出所、信頼度、文書種別、隔離
- 計画時: 利用者目的と許可された操作の一致
- 道具実行前: 引数、権限、上限、承認
- 出力時: 機密情報、不適切内容、引用、Schema
- 実行後: 実際の変更、重複、監査記録
危険な副作用は、実行前の制御で止める。
プロンプト抽出と権限侵害を分ける
利用者が内部指示を尋ねる試みは、Instruction Hierarchyの評価として有用である。しかし、内部プロンプトが漏れたことと、秘密データや外部操作へ到達できたことは影響が異なる。
プロンプトが公開されても安全な構成を目指す。認証情報、顧客データ、管理者だけの値をsystem promptへ置かず、道具の認可をコードで行う。
一方で、内部指示には安全方針、検出規則、製品設計が含まれる場合があるため、不要に公開しない。抽出耐性と実際の権限境界を別々に試験する。
保存データから攻撃が続く場合がある
攻撃文を会話履歴、メモリ、要約、RAG索引へ保存すると、元の文書を削除しても後続のセッションで再び読み込まれる。エージェントが別の文書やコメントへ攻撃文を書き込み、利用者間で広がる可能性もある。
長期保存へ入れる前に、出所、利用目的、所有者、信頼レベルを記録する。モデルが生成した「記憶」を事実や指示として無条件に昇格しない。
事故時には、元の入力だけでなく、派生した要約、埋め込み、キャッシュ、評価データ、出力先を追跡して削除または隔離する。文書IDと版をトレースへ残すと影響範囲を調べやすい。
テストは攻撃文の検出率だけで終えない
評価では、モデルが攻撃文へ従ったかだけでなく、危険な影響がアプリで止まったかを見る。
テスト対象には次を含める。
- 利用者入力からの直接攻撃
- Web、メール、PDF、検索、道具結果からの間接攻撃
- 複数文書へ分割した指示
- 多言語、画像内文字、引用文に見せた命令
- 他の利用者の資源ID指定
- 任意URLや外部宛先への送信
- 承認前後の引数変更
- 取消後と再試行時の二重実行
- 会話履歴や長期記憶からの再出現
合格条件は、危険な道具が実行されない、秘密が送られない、権限外データへ到達しない、判定不能なら停止することである。攻撃に対してモデルが丁寧に拒否文を書いたかだけを評価しない。
モデル、developer指示、道具Schema、検索、MCP、権限を変更したら回帰試験を行う。本番で止めた例も、データ方針を確認して評価セットへ戻す。
プロンプトインジェクション対策の中心は、モデルに絶対だまされない文章を書かせることではない。未信頼データが命令として解釈される可能性を受け入れ、読解と行動を分け、自由文の伝播を減らし、秘密と高権限の道具を遠ざけることである。検出が一度失敗しても、権限確認、承認、送信先制限で実害を止められる多層構成が必要になる。