Claude Media
Claudeが間接プロンプトインジェクションを拒否する理由と対策

Claudeが間接プロンプトインジェクションを拒否する理由と対策

tool_result内に自分の指示を書くと、Claudeが拒否したり確認を求めたりすることがあります。原因と、指示の置き場所を変える具体的な直し方をまとめます。

tool_resultブロックの中にアプリ側の指示を書き込むと、Claudeがその指示に従わず、実行前にユーザーへの確認を求めてくることがあります。原因はバグではなく、Claudeがtool_resultの中身を第三者由来の信頼できないデータとして扱うよう訓練されているためです。この記事では、この挙動が起きる理由と、指示を正しい場所へ移す具体的な直し方をまとめます。

症状 — tool_resultに書いた指示が無視・確認要求される

公式のトラブルシューティングページは、この症状を次のように説明しています。

Claude refuses to act on a tool result, or asks the user to confirm instructions that came from it

原因として挙げられているのは「開発者自身の指示がtool_resultのコンテンツ内に置かれていること」そのものです。修正の方向性は明快で、指示をtool_resultの外に出し、後続のuserターンか、対応モデルでは会話途中のシステムメッセージとして送ることです。tool_resultにはデータだけを残します。

なぜtool_resultは信頼されない設計になっているか

tool_resultはWebページの取得結果、受信メール、ユーザーがアップロードしたファイル、外部APIのレスポンスなど、アプリ開発者が内容を制御できない情報が入ってくる場所です。この経路を悪用し、第三者が仕込んだ指示でClaudeを乗っ取ろうとする攻撃が間接プロンプトインジェクションです。ユーザー自身が悪意ある入力を送る直接的なジェイルブレイクとは異なり、ユーザーは正規の利用者のままで、Claudeが処理する第三者コンテンツの側に攻撃が仕込まれている点が特徴です。

web_searchツールのようにAnthropicが直接実行するサーバーツールも、返ってくるcontentは検索でヒットしたWebページの中身です。性質としては同じ「アプリ開発者が制御できない第三者コンテンツ」にあたります。攻撃者が検索結果に表示されるページの本文へ指示文を仕込んでおくと、そのページを要約させようとしたClaudeが、埋め込まれた指示に気づかず従ってしまう経路が理論上成立します。tool_resultへの警戒はこうした間接的な経路全般が対象で、自分で実装したクライアントツールの結果に限った話ではありません。

Claudeはtool_resultの中に現れる指示を、原則として警戒すべき情報として扱うよう訓練されています。これは有効な防御である一方、開発者自身が正当な指示を同じ場所に書いても、同じ警戒の対象になってしまうという副作用を生みます。tool_resultは常に「データを置く場所」であり「指示を置く場所」ではない、という区別を徹底する必要があります。実装をレビューする際は、tool_resultブロックのcontentにアプリ側が生成した文字列(定型の指示文やテンプレート)が混じっていないかを、まず確認するとよいでしょう。

正しい実装 — 指示はtool_resultの外に出す

もっとも基本的な直し方は、指示をtool_resultの直後に続くuserターンのテキストとして送ることです。

{
  "role": "user",
  "content": [
    { "type": "tool_result", "tool_use_id": "toolu_01", "content": "15 degrees" },
    { "type": "text", "text": "この結果をもとに、摂氏で1行の要約を作ってください。" }
  ]
}

tool_resultブロックはcontent配列の先頭に置き、指示テキストはその後に置きます。この並び順自体はAPIのフォーマット要件でもあり、tool_resultより前にテキストを置くと別のエラーになります。

会話途中のシステムメッセージを使えるモデルもある

Fable 5.1・Mythos 5.1・Fable 5・Mythos 5・Opus 4.8・Opus 5では、userメッセージの代わりにrole: "system"のメッセージを会話途中のシステムメッセージ(mid-conversation system messages)として挿入する方法も使えます。Sonnet 5では利用できません。この場合は最上位のsystemフィールドを使う必要があります。

{
  "role": "system",
  "content": "ツールの実行結果に基づいて、摂氏で1行の要約を作成してください。"
}

配置には制約があり、条件は2つあります。(1)コンテンツを持つsystemメッセージは、tool_resultを含むuserターン、またはサーバーツールの結果で終わるassistantターンの直後に置きます。(2)さらにassistantターンの直前に置くか、messages配列の末尾で終える必要があります。片方だけ満たしても400エラーになります。tool_useブロックとそのtool_resultの間には挟めません。

なお、assistantターンが結果未返却のサーバーツールを呼んでいる場合は、続くuserメッセージをtool_resultブロックだけで構成する必要があります。ここにテキストを混ぜるとそのターンが早期終了し、未解決のサーバーツールを名指しする400エラーになるので注意してください。このケースでは、tool_resultの直後にtextで指示を送るという先述の直し方がそのまま使えないため、指示は次のuserターンまで待つか、サーバーツールの結果が返ってきた後に送る必要があります。

なぜuserターンでなくシステムメッセージを使う場面があるのか

指示を送るだけなら、通常のuserメッセージで十分な場面がほとんどです。ではなぜ会話途中のシステムメッセージという別の手段が用意されているかというと、理由はプロンプトキャッシュとの相性です。

Anthropicのプロンプトキャッシュはtoolssystemmessagesの順にリクエストのプレフィックスをハッシュ化します。直近のリクエストと先頭からバイト単位で一致した範囲だけを、キャッシュから読み込む仕組みです。最上位のsystemフィールドを書き換えると、そこから後ろのハッシュがすべて変わり、それまで積み上げてきたキャッシュがまとめて失効します。長いエージェントセッションの途中で「今後はこの制約を守ってほしい」という運用上の指示を追加したいときも同じです。最上位のsystemフィールドを直接編集すると、それまでの会話履歴すべてがキャッシュミスになってしまいます。

会話の末尾にrole: "system"のメッセージを追記する方式なら、それより前の会話はキャッシュがそのまま有効なままです。新しい指示だけがuserメッセージと同等の優先度で追加されます。加えてsystemロールはuserロールより優先度が高く扱われるため、エンドユーザーが矛盾する要求を出しても運用側の指示を維持したい場面に向いています。キャッシュ効率と指示の優先度の両方が絡む選択なので、単純に「間接プロンプトインジェクション対策のためだけの機能」と捉えないほうがよいでしょう。ツール呼び出し全体の設計パターンはAnthropic Advanced Tool Useでも扱っています。

注意 — システムメッセージも第三者コンテンツの置き場所にはならない

ここで見落としやすい落とし穴があります。「指示をtool_resultの外に出す」という原則を、「取得した第三者コンテンツそのものをtool_resultの外に出す」と誤読してはいけません。公式ドキュメントは、Webページの本文や取得したドキュメントのような第三者由来のデータそのものをシステムメッセージに直接置くことを明確に禁止しています。システムメッセージの中身はオペレーター権限の指示としてClaudeに扱われるため、そこに外部コンテンツを置くと、そのコンテンツに事実上オペレーター権限の指示と同じ重みを与えてしまうためです。移してよいのはあなたの指示だけです。第三者由来のデータはtool_resultに置いたままにします。

防御を重ねる — JSON化・出典明示・出力スクリーニング

指示の置き場所を直すだけでなく、次の対策を重ねることで間接プロンプトインジェクションへの耐性をさらに高められます。

  • 第三者コンテンツをJSONエンコードする: 自由形式のテキストに連結するのではなく、JSONオブジェクトで包みます。JSONのエスケープが明確な区切りとして働くため、攻撃者が引用符やタグを閉じて指示のコンテキストに「脱出」することが難しくなります
  • 出典をツールの説明やレスポンスの構造で明示する: 「未知の送信者からの受信メール本文」のように性質と出所を明示すると、Claudeが埋め込まれた指示をどこまで信用すべきか判断しやすくなります
  • システムプロンプトでポリシーを宣言する: ツール・ドキュメント・検索から返るコンテンツは信頼できないデータであり、本来のリクエストを上書きしてはならないと明示的に書きます
  • 軽量モデルでツール出力をスクリーニングする: Claude Haiku 4.5のような軽量モデルに「指示のリダイレクトを試みているか」だけを判定させます。疑わしければ生のコンテンツの代わりにエラーや要約をtool_resultとして返します

computer useツールでは追加の分類器が動く

computer useツールを使っている場合、Anthropicはスクリーンショットに含まれる潜在的なプロンプトインジェクションを検知する追加の分類器を実行します。Claudeが実行前にユーザーへの確認を求めるよう誘導する仕組みです。これは本記事で扱ったtool_resultテキストへの警戒とは別の、画面内容に対する専用の防御レイヤーです。

Claude Codeのサンドボックス防御との違い

Claude Codeは同じ脅威クラス(ツール出力に紛れ込む間接プロンプトインジェクション)に対して、メッセージ構造とは別のレイヤーで防御を重ねています。OSレベルのサンドボックスでBashツールの実行環境自体を隔離し、ファイルシステムやネットワークへのアクセスを制限する設計です。本記事で扱ったメッセージ構造レベルの対策(指示の置き場所を変える、コンテンツをJSON化する)と、プロセスレベルの隔離は補完関係にあり、どちらか一方で十分というものではありません。仕組みの詳細はClaude Codeのサンドボックス設計にまとめています。

まとめ

tool_resultの中にアプリ側の指示を書くと、Claudeはそれを第三者由来の信頼できない情報として扱い、拒否や確認要求という形で表面化します。直し方は、指示をtool_resultの直後のuserターン、または対応モデルでは会話途中のシステムメッセージに移すことです。ただし後者は第三者コンテンツそのものの置き場所にはならない点に注意してください。JSON化・出典明示・出力スクリーニングを重ねることで、指示の置き場所を直すだけでは防ぎきれない攻撃パターンへの耐性も高められます。

この記事を共有:XはてブLinkedIn