エージェントを盲信するのはやめよう:承認を特定のツール呼び出しに紐付ける
ほとんどのエージェント型システムは、ファイルの書き込みや送金といった危険なアクションを、単純な「承認」によって保護しています。
通常、この承認はシステム状態内のブール値(boolean)フラグとして扱われます。
例:approved: true。
これは間違いです。ブール値には、攻撃者に悪用される3つの欠陥があります。
- 反転 (Flip): 攻撃者がプロンプトインジェクションやコードの欠陥を利用して、状態を
falseからtrueに書き換える。 - 再実行 (Replay): ユーザーが「ファイルを読み込む」といった安全なコマンドを承認する。システムは「
true」を検知し、その後に続く「データベースを削除する」といった2番目の危険なコマンドも許可してしまう。 - 引数のドリフト (Argument Drift): ユーザーが「10ドル送金」を承認する。攻撃者が実行前に金額を10,000ドルに変更しても、フラグは依然として「
true」のまま。
問題は、承認をセッション全体のプロパティとしてモデル化していることです。承認は、特定の1回限りの呼び出しに対する「証拠」である必要があります。
解決策:
人間が呼び出しを承認する際、セキュアなタグを作成します。このタグは、以下の4つの要素をロック(固定)しなければなりません。
- 一意のツール呼び出しID(tool call ID)。
- 正確な引数のハッシュ値。
- ユーザーの識別情報。
- 有効期限。
実行のまさにその瞬間に、このタグを検証します。システムのみが知る秘密鍵(secret key)を使用してください。
実装にあたっては、以下のルールに従ってください。
- 正規化 (Canonicalization) を使用する: 承認者と実行者の両方が、全く同じバイト列をハッシュ化する必要があります。数値やキーが一致するように、RFC 8785を使用してください。
- フェイルクローズ (Fail Closed): タグが存在しない、期限切れ、または不正な場合は、特定の「未承認(not approved)」エラーを返してください。標準的なツールの実行結果として扱ってはいけません。
- デフォルト拒否 (Deny by Default): 明示的な承認が必要なツールのみを許可し、それ以外はすべて拒否します。
- 再実行への対策 (Handle Replays): Temporalのようなエンジンを使用している場合は、秘密鍵が決定論的(deterministic)であることを確認してください。システム再起動後に鍵が変わってしまうと、既存のすべての承認が無効になってしまいます。
認可(Authorization)は、単なる浮遊した状態であってはなりません。「この特定の人物が、この特定のツールに対し、この特定の引数について、この特定の時刻まで承認した」ということを証明する、紐付けられたエンベロープ(封筒)であるべきです。
ブール値の使用はやめましょう。それは簡略化ではなく、バグなのです。
Optional learning community: https://t.me/GyaanSetuAi