iT邦幫忙

2026 iThome 鐵人賽

DAY 17
0
AI Security

合法呼叫湊出的攻擊鏈:AI Agent 防禦的 30 天觀念養成系列 第 17

Day 17|什麼是Confused Deputy?要怎麼去確認與優化Agent的決策正確性?

  • 分享至 

  • xImage
  •  

前言

這個系列寫到現在,我一直在同一個假設底下討論:有一個 Agent 代表某位使用者行事,而我要判斷它的每一步動作能不能發生。前面談過的能力(Capability)、來源追溯(Provenance)、Legitimacy Reference,還有那個會反咬一口的回應,處理的都是「這個動作可不可以做」。

這一篇換個角度,看權限本身。Agent 手上能開信箱、改資料庫、付款的權限,有些是使用者委任給它的,有些是 Agent 自己持有的。一次操作裡可能同時牽涉好幾份來源不同的權限;如果執行環境(runtime)沒有把它們分開,Agent 就可能拿自己的權限,去完成別人指定的事。這類問題早在 1988 年就有了名字,叫 Confused Deputy(混淆代理)。

一個跟提示注入無關的老例子

先從 Norm Hardy 在 1988 年寫下的例子講起。我刻意挑這個完全不涉及提示注入(Prompt Injection)的案例,是想先把 Confused Deputy 的結構單獨拿出來看,不和前面幾篇的內容混在一起。

Hardy 回顧的是 Tymshare 分時系統上的一個編譯器。這個編譯器放在系統目錄 SYSX 底下,執行時會把語言功能的使用統計寫進同一個目錄裡的 STAT 檔,所以系統給了它寫入 SYSX 目錄的權限。另一方面,使用者呼叫編譯器時可以指定一個檔名,讓編譯器把除錯資訊輸出到那個檔案。

麻煩在於,記錄計費資料的 BILL 檔也放在 SYSX 裡。某位使用者把輸出檔名指定成 (SYSX)BILL,編譯器照做,計費資料就被除錯資訊蓋掉了:

使用者指定輸出檔:(SYSX)BILL
        ↓
編譯器用自己對 SYSX 的寫入權限開檔
        ↓
作業系統檢查:編譯器可以寫入 SYSX → 通過
        ↓
BILL 被除錯資訊覆寫

整個過程沒有任何一步繞過權限檢查,編譯器也沒有違反自己的程式邏輯。作業系統只知道「這個程式可以寫入 SYSX」,無從得知這次寫入是在替使用者輸出除錯資訊,還是編譯器在寫自己的統計檔。編譯器同時帶著兩份來源不同的權限,卻沒有辦法表達「這次寫檔用的是哪一份」。

Hardy 提出的解法是能力(Capability):把「要寫到哪裡」和「憑什麼可以寫」綁在同一個東西上。編譯器寫 STAT 時,用的是自己持有的能力;寫使用者指定的輸出檔時,則必須使用使用者交給它的能力。使用者本來就沒有寫入 BILL 的能力,自然也沒辦法透過編譯器寫進去。反過來說,單純把編譯器的權限收小也行不通,因為它確實需要寫入 SYSX。問題出在權限被使用時,沒有標明是替誰使用。

Agent 如何成為現代版的 Deputy

換到 Agent 身上,結構幾乎一樣。Agent 可能持有 Gmail 的 token、資料庫連線、付款 API 憑證,也可能把其中一部分權限再委任給其他 Agent 或工具;使用者則提供任務和操作對象。代理者手上有權限,要操作的目標卻可能來自別人。

這裡要先分清兩件事:「這個工具能不能用」,以及「這一次、帶著這組參數的呼叫能不能執行」。

假設 Agent 有一個 update_customer_email 工具。執行環境可以事先決定 Agent A 看不看得到這個工具,但具體參數要等模型決定後才會出現,例如:

update_customer_email(customer="Alice", email="attacker@example.com")

工具可以用,不代表模型給出的每一組參數都應該直接執行。授權檢查到了這一步,才有了具體的檢查對象。

OpenAI Agents SDK(Python 版)的文件也把這兩件事分開處理。工具的 is_enabled 參數決定工具對模型是否可見、能不能被分派,判斷依據是執行脈絡,拿不到這次呼叫的參數。文件明確說明,is_enabled 不能取代依工具參數或所存取資源而做的授權檢查;這類檢查應該寫在工具實作裡,或交給工具輸入防護(tool input guardrails)與核准(approval)機制。後面談逐次核准時會再回到這個 SDK。

接著看一個完整的情境。使用者請 Agent 整理客戶資料,Agent 在過程中讀到一份外部文件,文件內容誘導它去修改 Alice 的 email:

使用者:「整理客戶資料」
        ↓
Agent 讀取外部文件,被其中的內容誘導
        ↓
Agent 呼叫 update_customer_email(customer="Alice", email="attacker@example.com")
        ↓
工具端檢查
  ・憑證合法
  ・Agent 被允許呼叫這個工具
  ・參數內容由 Agent 決定,沒有另外檢查
        ↓
修改執行

在這個情境裡,追問「Agent 有沒有修改 email 的權限」幫助不大。它很可能有,否則正常工作也做不了。比較有用的問題是:這一次修改,行使的是誰的權限?

合法憑證只能證明 Agent 有能力呼叫這個工具,沒辦法證明使用者要求它改這個地址。身份驗證與委任鏈可以回答「誰在代表誰」、「權限從哪裡來」;可是只要具體操作仍由一個可能被操控的 Agent 決定,工具端看到的就還是一個合法請求。

2026 年 Zuvic 的一篇研究直接測了這一點。研究對象是 LangChain/LangGraph、LlamaIndex 和 Stripe Agent Toolkit,檢查它們會不會根據模型實際產生的參數,對每一次工具呼叫重新授權。在研究者測試的公開版本與設定下,這些框架預設都有某種能力限制,但都沒有「逐次依參數授權、檢查不過就拒絕執行」的機制。研究者示範了一個未經授權的 Payout 呼叫:在預設的分派流程中可以執行,加上他們設計的 ScopeGate 控制後則被拒絕。作者也特別註明,這不是在指稱這些框架存在 CVE 等級的漏洞。這篇研究呈現的是,至少在測試的版本與設定中,「工具可用」與「這次呼叫已被重新授權」是兩個分開的控制點,而框架預設只提供了前者。

權限不應沿著呼叫鏈往下傳

Model Context Protocol(MCP)的授權規格直接用 Confused Deputy 描述了一種風險:token 直接轉送(token passthrough)。如果 MCP 伺服器收到用戶端的 token 之後,原封不動地轉送給下游服務,下游 API 可能誤以為這份 token 來自 MCP 伺服器,或以為它已經在上游驗證過。

因此規格要求 MCP 伺服器必須驗證收到的存取 token 確實是發給自己的;如果伺服器還需要呼叫其他 API,應該另外取得針對那個 API 的 token,不能把用戶端的 token 直接往下傳:

允許:MCP 用戶端 ──(Token A)──→ MCP 伺服器 ──(Token B)──→ 下游 API
禁止:MCP 用戶端 ──(Token A)──→ MCP 伺服器 ──(Token A)──→ 下游 API

Token A 能被 MCP 伺服器接受,只表示用戶端有權呼叫 MCP 伺服器;它不應該自動變成 MCP 伺服器呼叫另一個服務時的權限。

這條規定管的範圍,是 token 發給誰、權限從哪裡來、能不能帶到下一個服務。它沒有處理另一個更難的問題:Agent 拿著一份來源正確、對象也正確的 token,做出來的具體操作是否符合使用者原本交辦的任務。token 傳遞得當,Agent 仍然可能在合法 token 之下做出任務範圍以外的事。

最小權限能管到哪裡

前面幾篇談過最小權限,它在這裡依然重要,只是單靠它處理不了 Confused Deputy。

假設一個 Agent 被允許使用 read_customerupdate_customer_emailsend_email 三個工具。拿掉 update_customer_email,它當然就不可能被誘導去改 email;但如果正常工作本來就需要改 email,這個做法就行不通。執行環境還得知道這次操作涉及的資源、動作、參數、委任者,以及委任的範圍。

兩者處理的問題不同。最小權限回答「最多能碰什麼」,限制的是能力的上限;混淆代理的防禦要回答「這一次用的是哪一份權限」,並確認這個具體操作落在那份權限的範圍內。Hardy 的編譯器確實需要寫入 SYSX,Agent 也可能確實需要修改客戶資料。Confused Deputy 不一定來自多給了不必要的權限,也可能是同一份必要的權限,被拿去處理來源不同的指定目標。

回到 Alice 的例子。使用者只說了「整理客戶資料」,被外部內容影響的 Agent 卻呼叫了修改 email 的工具。這時 token 是真的,委任鏈是真的,工具也確實允許 Agent 呼叫 update_customer_email。缺少的是一個判斷:這個具體操作,是否在使用者委任的任務範圍內。

前幾篇提過的動態撤權、參照與執行脈絡判斷,可以在執行過程中加上限制。可是如果「讀文件,然後修改客戶資料」本來就是某個 Agent 的合法工作流程,攻擊者誘導出來的路徑,看起來未必有什麼異常。

所以要檢查的對象,得從「這個 Agent 能不能呼叫這個工具」,往前推到「這個 Agent 現在提出的這一次呼叫,是不是它被授權要做的事」。前面提到 OpenAI Agents SDK 把工具可見性和依參數的授權分在不同階段處理,對應的就是這個落差。到了這一層,問題已經落在執行環境的流程設計上,光靠提升模型本身的安全性處理不了。

工具執行時,資源邊界也可能失守

再往下一層,還有一個更直接的工程問題:就算執行環境已經確認工具可以使用,工具本身對資源和參數的限制如果寫錯,一樣會出事。

MCP 官方的 servers 儲存庫(modelcontextprotocol/servers)有實際的安全公告可以參考。以 mcp-server-git 為例,2026 年曾被發現 git_add 沒有檢查 files 參數是否仍在 repository 範圍內。攻擊者可以傳入 ../ 形式的相對路徑,把 repository 以外的檔案加進 Git index,之後再經由 commit、push 把資料帶出去。修補後的版本改用 Git CLI 本身的檢查,拒絕 repository 以外的路徑。

Agent 呼叫 git_add(files=["../secret.txt"])
        ↓
工具:參數驗證、資源邊界檢查(此處缺漏)
        ↓
檔案系統操作
        ↓
repository 以外的檔案被加入 index

嚴格來說,這個案例不屬於 Confused Deputy:它沒有出現「使用者授權了 A,Agent 卻把權限用在 B」的情況。它說明的是另一件事:工具不能假設模型給的資源參數一定落在權限範圍內,資源邊界必須在執行層實際檢查。邊界沒有被強制執行時,Agent 手上合法的工具能力,就可能碰到原本不該碰的資源。

同一個儲存庫目前還列出 mcp-server-git 的其他漏洞,包括不受限制的 git_initgit_diffgit_checkout 的參數注入,以及其他路徑驗證繞過。這些同樣不是 Confused Deputy 的直接例證,但可以看出工具的資源邊界與參數檢查,不能只停留在模型判斷或工具描述的層次。另外要注意,這個儲存庫自己說明,裡面的伺服器是用來展示 MCP 功能與 SDK 用法的參考實作,不應視為可直接上線的方案,所以不能從這幾個漏洞推論所有 MCP 部署都有相同問題。

假設模型一定會被攻陷

如果一直假設「只要提示注入防得好,Agent 就不會亂來」,安全邊界最後還是落回模型身上。Dantuluri 與 Sundi 在 2026 年針對多 Agent 委任結構的研究,採取相反的前提:模型本身不可信。

他們提出的要求是,即使 Agent 的模型輸出已經完全被攻擊者控制,系統也不能讓它超出被明確委任的權限。研究把 Confused Deputy、token 竊取與重放、提示注入造成的權限提升,以及遭入侵的子 Agent 放進同一個威脅模型,並提出幾項安全要求,其中包括不依賴模型的強制執行。他們也實作了一個授權仲介(broker),讓授權決策在模型之外完成:

模型輸出(假設已被完全控制)
        ↓
授權仲介:檢查委任範圍、委任來源、發送者身分,並強制執行授權規則
        ↓
允許 → 工具執行
拒絕 → 不執行

在作者設定的實驗環境與威脅模型下,20 萬個偽造 token 全數被拒絕。另一組實驗隨機產生 2,000 個情境:採用 bearer 型委任(持有 token 即可行使權限)時,被攻陷的子 Agent 可以觸及全部 8,100 個動作;改用受限委任後,平均只能觸及 1.5 個。單次授權判斷的量測成本約 2.6 微秒。這些數字來自作者自己的實驗設定,換到其他 Agent 執行環境,改善幅度不見得相同。

這篇研究有意思的地方,在於把「不要相信模型」變成一個可以量測的執行環境性質。模型可以被騙,但被騙之後最多能碰到什麼,不該由模型自己決定。

把具體操作連回使用者的請求

身份、最小權限和執行路徑各自處理一部分問題,還缺一個控制點:讓每個具體操作可以追溯到一個被授權的意圖,而不只是追溯到一份合法憑證。

目前已經有幾個研究與工程上的嘗試,但都還在發展中,談不上有標準答案。

第一個是 2026 年的 IETF Internet-Draft LIP-4D,提出一套實驗性的意圖脈絡與授權對話協定。它不打算取代工作負載身分(workload identity)、OAuth 或既有的授權框架,而是在這些機制之上加入結構化的意圖與證據。文件中的「意圖」可以包含動作、目的、執行脈絡、證據、時間限制和演變歷史;政策執行點(policy enforcement point)根據這些資訊,發出短效、與意圖綁定的授權,或者拒絕請求。

一種比較直覺的做法,是把使用者的原始請求算出雜湊值,當成授權憑證一路帶著走。問題是雜湊只能證明請求沒被竄改,說明不了哪些動作在範圍內。LIP-4D 的做法是把意圖拆成動作、資源、參數、限制條件等欄位,並把意圖在執行過程中的演變也納入授權判斷的依據。概念上的流程大致如下:

使用者意圖
    ↓
與意圖綁定的授權
    ↓
Agent 產生具體動作
    ↓
執行環境檢查:權限、委任、資源、參數、意圖
    ↓
工具

要注意的是,LIP-4D 目前只是 Internet-Draft,文件本身標示為 Experimental,並不是 IETF 正式通過、已被廣泛部署的標準。它提供的是一個設計方向:先把意圖結構化,再交給政策執行點使用。

第二個方向是「先起草、再提交」(Draft-Then-Commit):高風險操作先產生草稿,實際造成副作用的提交步驟另外檢查。ACL 2026 的 VIGIL 提出 Verify-Before-Commit,讓 Agent 先產生候選動作,經過額外的驗證階段後,才決定是否進入實際的 Action Commit。作者建立了 SIREN 評測集,收錄 959 個工具串流注入(tool-stream injection,指惡意指令藏在工具回傳的內容裡)案例,並報告在他們的實驗中,VIGIL 相較於比較對象中的動態防禦方法,攻擊成功率降低超過 22%。VIGIL 處理的是這類注入情境下的動作驗證,不能直接視為 Confused Deputy 的解法。把這個方向放進執行環境的流程來看:

Agent 產生草稿或意圖
        ↓
外部驗證:權限、委任範圍、執行脈絡、意圖
        ↓
通過 → 提交 → 工具執行,產生實際副作用
不通過 → 拒絕

第三個是已經可以使用的工程機制。前面提到的 OpenAI Agents SDK 可以對工具設定 needs_approval,核准規則會拿到這次呼叫解析後的參數,據此決定是否需要批准。模型產生工具呼叫後,Runner 會先暫停,讓外部程式或使用者核准或拒絕這次呼叫,再繼續執行。以下是文件中的範例,主旨含有 refund 的郵件需要人工審查:

from agents.decorators import tool

async def requires_review(_ctx, params, _call_id):
    return "refund" in params.get("subject", "").lower()

@tool(needs_approval=requires_review)
async def send_email(subject: str, body: str) -> str:
    return f"Sent '{subject}'"

這個機制的價值在控制點的位置:它插在模型產生具體參數之後、實際副作用發生之前。

不過控制點放對了位置,規則本身寫得如何又是另一回事。如果核准規則只是「只要是 send_email 就放行」,那仍然只是工具層級的控制;要進一步檢查收件人、目的、資源、委任來源和任務範圍,才開始接近逐次授權。把授權判斷移到模型之外是必要的一步,但光有這一步還不夠。

四個相關但不同的問題

把前面幾節放在一起,大致可以整理出四個問題:

面向 要回答的問題 本文提到的相關機制
Capability 最多能碰什麼? 最小權限、工具可見性(如 is_enabled
Provenance 這份權限從哪裡來? MCP 的 token 對象驗證與禁止轉送、委任鏈、授權仲介
Context 放在目前的執行脈絡裡,這個動作合不合理? 動態撤權、執行脈絡判斷(見前幾篇)
Intent 這個具體動作是否在被授權的請求範圍內? 依參數的核准規則、LIP-4D、Draft-Then-Commit

這張表只是粗略的對應,同一個機制常常同時涉及好幾個面向。例如授權仲介除了檢查委任來源,也限制了子 Agent 能觸及的動作範圍。

四個問題裡,Intent 最難處理。假設使用者說「幫我整理這些客戶資料」,Agent 還是得自己決定怎麼排序、查哪些資料、摘要怎麼寫,甚至根據中間結果決定下一步要呼叫什麼工具。意圖不可能細到每一次 API 呼叫的每個參數都由使用者事先寫好。反過來,如果每個高風險操作都要使用者重新確認,安全性提高了,Agent 的自主性也跟著下降。

比較實際的做法,是讓使用者授權一個任務,Agent 在任務範圍內自行拆解,執行環境則在每個具體動作發生前做檢查:

使用者授權任務(形成意圖與授權)
        ↓
Agent 自行拆解任務
        ↓
產生具體動作
        ↓
執行環境檢查:權限、委任、資源、參數、意圖

這樣一來,問題就變成:使用者授權了一個任務之後,Agent 在執行過程中可以自行決定到什麼程度?LIP-4D 值得注意的原因之一,是它把動作、資源、參數、限制條件和意圖的演變都當成政策執行點可以使用的資料,沒有假設一段自然語言本身就足以構成完整的授權政策。但這仍是研究與設計階段的問題,目前沒有被各家 Agent 執行環境普遍採用的標準做法。

小結

前面幾篇一直在問「這個動作該不該執行」,Confused Deputy 則把問題往前推一步:這個動作行使的是誰的權限?Hardy 在 1988 年描述的編譯器沒有被攻破,作業系統的權限檢查也照常運作;出問題的地方在於同一個程式帶著不同來源的權限,而指定目標和權限之間沒有綁在一起。到了 Agent 身上,這種結構更容易出現:合法的憑證、合法的工具,加上由模型決定的操作內容,可能組合出一個形式上合法、卻不該發生的動作。

現有的機制各自處理其中一部分。MCP 規範了 token 的接收對象與轉送,避免權限沿著呼叫鏈被誤用;Agent SDK 提供了工具輸入檢查、依參數與資源的授權,以及逐次核准;研究則開始在實際框架和多 Agent 委任環境中,量測能力限制與逐次授權之間的落差,以及把授權放到模型之外的效果。最小權限、身份與委任來源的管理仍然要做,模型之外的強制執行也不能少。

感謝大家今日份的閱讀。

參考資料

  • Hardy, N. (1988). The Confused Deputy (or why capabilities might have been invented). ACM SIGOPS Operating Systems Review, 22(4), 36–38.
  • Dantuluri, P. S. V., & Sundi, J. (2026). Delegation Without Trust: An Empirical Gap Analysis of Identity, Authorization, and Runtime Governance in Multi-Agent LLM Systems. arXiv:2609.00267.
  • Zuvic, D. M. (2026). Capability Gates Are Not Authorization: Confused-Deputy Failures in LLM Agent Frameworks. arXiv:2606.28679.
  • Lin, J., et al. (2026). VIGIL: Defending LLM Agents Against Tool-Stream Injection via Verify-Before-Commit. ACL 2026.
  • Model Context Protocol. Authorization. MCP Specification.
  • Model Context Protocol. Security Advisories — modelcontextprotocol/servers.
  • OpenAI Agents SDK (Python). Tools. 官方文件。
  • OpenAI Agents SDK (Python). Human-in-the-loop. 官方文件。
  • OpenAI Agents SDK (Python). Guardrails. 官方文件。
  • Ovidi, A. (2026). LIP-4D: An Intent Context and Authorization Dialogue Protocol for Autonomous Agents. Internet-Draft, draft-ovidi-lip-4d-00.

上一篇
Day 16|Runtime 對惡意指示有了反應後,這個反應本身也成了攻擊面
下一篇
Day 18|開始分析 utility 與 security 之前:先檢查 Harness 修改與基礎設施錯誤
系列文
合法呼叫湊出的攻擊鏈:AI Agent 防禦的 30 天觀念養成18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言