エージェント委任
AIエージェントが互いに対話することが増える中 — あるエージェントが別のエージェントを 呼び出してサブタスクを完了する — 新しいクラスのセキュリティリスクが生まれます。 エージェントチェーンは、制限の少ないエージェントを通じてデータをロンダリングし、 分類コントロールを回避するために使用できます。Triggerfishは暗号化されたエージェント アイデンティティ、分類上限、および必須のtaint継承でこれを防ぎます。
エージェント証明書
Triggerfishのすべてのエージェントは、そのアイデンティティ、機能、委任権限を定義する 証明書を持っています。この証明書はエージェントのオーナーによって署名され、エージェント 自身または他のエージェントによって変更できません。
json
{
"agent_id": "agent_abc123",
"agent_name": "Sales Assistant",
"created_at": "2025-01-15T00:00:00Z",
"expires_at": "2026-01-15T00:00:00Z",
"owner": {
"type": "user",
"id": "user_456",
"org_id": "org_789"
},
"capabilities": {
"integrations": ["salesforce", "slack", "email"],
"actions": ["read", "write", "send_message"],
"max_classification": "CONFIDENTIAL"
},
"delegation": {
"can_invoke_agents": true,
"can_be_invoked_by": ["agent_def456", "agent_ghi789"],
"max_delegation_depth": 3
},
"signature": "ed25519:xyz..."
}証明書の主要フィールド:
| フィールド | 目的 |
|---|---|
max_classification | 分類上限 — このエージェントが操作できる最高taintレベル。INTERNAL上限を持つエージェントはCONFIDENTIALにtaintされたセッションによって呼び出されることができません。 |
can_invoke_agents | このエージェントが他のエージェントを呼び出す権限を持つかどうか。 |
can_be_invoked_by | このエージェントを呼び出せるエージェントの明示的な許可リスト。 |
max_delegation_depth | エージェント呼び出しチェーンの最大深さ。無制限の再帰を防ぎます。 |
signature | オーナーからのEd25519署名。証明書の改ざんを防ぎます。 |
呼び出しフロー
あるエージェントが別のエージェントを呼び出すとき、ポリシーレイヤーは呼び出し先エージェントが 実行する前に委任を確認します。チェックは決定論的でコードで実行されます — 呼び出し元 エージェントは決定に影響できません。
この例では、エージェントAはCONFIDENTIALのセッションtaintを持っています(以前に Salesforceデータにアクセスしたため)。エージェントBはINTERNALの分類上限を持っています。 CONFIDENTIALはINTERNALより高いため、呼び出しはブロックされます。エージェントAの taintされたデータは低い分類上限を持つエージェントに流れることができません。
セキュリティ ポリシーレイヤーは呼び出し元の現在のセッションtaintを
チェックします。上限ではありません。エージェントAがCONFIDENTIAL上限を持っていても、 重要なのは呼び出し時のセッションの実際のtaintレベルです。エージェントAが分類された データにアクセスしていない場合(taintがPUBLIC)、問題なくエージェントB(INTERNAL上限) を呼び出せます。 :::
委任チェーン追跡
エージェントが他のエージェントを呼び出すとき、完全なチェーンが各ステップのタイムスタンプと taintレベルで追跡されます:
json
{
"invocation_id": "inv_123",
"chain": [
{
"agent_id": "agent_abc",
"agent_name": "Sales Assistant",
"invoked_at": "2025-01-29T10:00:00Z",
"taint_at_invocation": "CONFIDENTIAL",
"task": "Summarize Q4 pipeline"
},
{
"agent_id": "agent_def",
"agent_name": "Data Analyst",
"invoked_at": "2025-01-29T10:00:01Z",
"taint_at_invocation": "CONFIDENTIAL",
"task": "Calculate win rates"
}
],
"max_depth_allowed": 3,
"current_depth": 2
}このチェーンは監査ログに記録され、コンプライアンスとフォレンジック分析のために クエリできます。どのエージェントが関与し、taintレベルがどうだったか、何のタスクを 実行したかを正確にトレースできます。
セキュリティ不変条件
4つの不変条件がエージェント委任を管理します。すべてはポリシーレイヤーのコードによって 強制され、チェーン内のどのエージェントによっても上書きできません。
| 不変条件 | 強制 |
|---|---|
| Taintはエスカレートのみ | 各呼び出し先はmax(自身のtaint, 呼び出し元のtaint)を継承します。呼び出し先は呼び出し元より低いtaintを持つことができません。 |
| 上限の遵守 | 呼び出し元のtaintが呼び出し先のmax_classification上限を超える場合、エージェントを呼び出すことができません。 |
| 深さ制限の強制 | チェーンはmax_delegation_depthで終了します。制限が3の場合、4番目レベルの呼び出しはブロックされます。 |
| 循環呼び出しのブロック | 同じチェーンでエージェントは2回現れることができません。エージェントAがエージェントBを呼び出し、エージェントBがエージェントAを呼び出そうとすると、2回目の呼び出しはブロックされます。 |
taint継承の詳細
エージェントA(taint: CONFIDENTIAL)がエージェントB(上限: CONFIDENTIAL)を正常に 呼び出すと、エージェントBはエージェントAから継承したCONFIDENTIALのtaintで開始します。 エージェントBが次にRESTRICTEDデータにアクセスすると、taintはRESTRICTEDにエスカレート します。この上昇したtaintは呼び出しが完了するとエージェントAに返されます。
Taintは両方向に流れます — 呼び出し時に呼び出し元から呼び出し先へ、完了時に呼び出し先から 呼び出し元へ。エスカレートのみ可能です。
データロンダリングの防止
マルチエージェントシステムの主要な攻撃ベクターはデータロンダリング — エージェント チェーンを使用して中間エージェントを通じてルーティングすることで、分類されたデータを より低い分類の宛先に移動することです。
攻撃
攻撃者の目標: PUBLICチャンネルを通じてCONFIDENTIALデータを漏洩
試みられたフロー:
1. エージェントAがSalesforceにアクセス(taint --> CONFIDENTIAL)
2. エージェントAがエージェントBを呼び出す(PUBLICチャンネルを持つ)
3. エージェントBがPUBLICチャンネルにデータを送信失敗する理由
Triggerfishはこの攻撃を複数のポイントでブロックします:
ブロックポイント1:呼び出しチェック。 エージェントBがCONFIDENTIAL以下の上限を 持っている場合、呼び出しは即座にブロックされます。エージェントAのtaint(CONFIDENTIAL)が エージェントBの上限を超えています。
ブロックポイント2:taint継承。 エージェントBがCONFIDENTIAL上限を持っていて呼び出しが 成功した場合でも、エージェントBはエージェントAのCONFIDENTIAL taintを継承します。 エージェントBがPUBLICチャンネルに出力しようとすると、PRE_OUTPUT hookがライトダウンを ブロックします。
ブロックポイント3:委任でのtaintリセットなし。 委任チェーン内のエージェントはtaintを リセットできません。taintリセットはエンドユーザーのみが利用でき、会話履歴全体をクリアします。 エージェントがチェーン中にtaintレベルを「洗う」メカニズムはありません。
データはエージェント委任を通じて分類を逃れることはできません。上限チェック、
必須のtaint継承、チェーン内でのtaintリセットなしの組み合わせにより、Triggerfishの セキュリティモデル内でエージェントチェーンを通じたデータロンダリングは不可能です。 :::
シナリオ例
シナリオ1:委任の成功
エージェントA(上限: CONFIDENTIAL、現在のtaint: INTERNAL)
エージェントB(上限: CONFIDENTIAL)を呼び出す
ポリシーチェック:
- AはBを呼び出せる? YES(BはAの委任リストにある)
- Aのtaint(INTERNAL)<= Bの上限(CONFIDENTIAL)? YES
- 深さ制限OK? YES(最大3の深さ1)
- 循環? NO
結果: 許可
エージェントBはtaint: INTERNALで開始(Aから継承)シナリオ2:上限によるブロック
エージェントA(上限: RESTRICTED、現在のtaint: CONFIDENTIAL)
エージェントB(上限: INTERNAL)を呼び出す
ポリシーチェック:
- Aのtaint(CONFIDENTIAL)<= Bの上限(INTERNAL)? NO
結果: ブロック
理由: エージェントBの上限(INTERNAL)がセッションtaint(CONFIDENTIAL)を下回るシナリオ3:深さ制限によるブロック
エージェントAがエージェントBを呼び出す(深さ1)
エージェントBがエージェントCを呼び出す(深さ2)
エージェントCがエージェントDを呼び出す(深さ3)
エージェントDがエージェントEを呼び出す(深さ4)
エージェントEのポリシーチェック:
- 深さ4 > max_delegation_depth(3)
結果: ブロック
理由: 最大委任深さを超えたシナリオ4:循環参照によるブロック
エージェントAがエージェントBを呼び出す(深さ1)
エージェントBがエージェントCを呼び出す(深さ2)
エージェントCがエージェントAを呼び出す(深さ3)
2回目のエージェントA呼び出しのポリシーチェック:
- エージェントAは既にチェーンに現れている
結果: ブロック
理由: 循環エージェント呼び出しが検出された関連ページ
- セキュリティファーストの設計 — セキュリティアーキテクチャの概要
- ライトダウン禁止ルール — 委任が強制する分類フロールール
- アイデンティティ & 認証 — ユーザーとチャンネルのアイデンティティの 確立方法
- 監査 & コンプライアンス — 委任チェーンが監査ログにどのように 記録されるか
