iT邦幫忙

2026 iThome 鐵人賽

DAY 29
0
AI Security

Microsoft Foundry 的 AI Agent 攻防實戰:建立高資安與可治理的 Agent 系統系列 第 29

Day 29|Microsoft Foundry 的 AI Agent 攻防實戰:Agent 說是工程司派來的,不能當簽章

  • 分享至 

  • xImage
  •  

Day 28 已把 fan-out、fallback 與 retry 納入 route budget,但共享 identity 還在。如果今天只在 receipt 補上 subject="alice",報表會變完整,安全性不會自動升級;任何 caller 都能在 JSON 裡打出同一行字。

大魔術熊貓工程司今天重跑 orchestrator → retriever → planner → executor。每個 Agent 即使位在同一個 Microsoft Foundry Project,也要重新驗上一輪。Agent 說「是工程司派我來的,不是黃易派我來的」,這不能當數位簽章。

先備名詞:簽章有效,只證明誰簽了,不證明他本來就有權

  • Issuer/Audience:issuer 是簽發者,audience 是 token 預定交給的接收者;接錯 audience 必須拒絕。
  • Root grant:由業務政策事先核發的根能力,明確綁 subject、tenant、action、resource、audience 與上限。只有「trusted issuer」不夠。
  • Attenuation(權限遞減):child delegation 只能維持或縮小 parent 能力,不能新增 action、resource、depth 或期限。
  • Nonce:一次性識別值;接收端必須用原子操作登記,並行驗證也只能有一次成功。
  • Hop-by-hop:每個接收 Agent 都重新驗 token 與自己的政策;上一跳驗過不能替下一跳背書。

Threat:Agent 名稱、conversation ID 與同一條網路都不是授權

一則 message 寫著 sender="planner",只證明 payload 裡有這個字串。Conversation ID、session ID、message ID 與 trace ID 用來關聯流程,也不證明 sender 真的是 planner。

就算 token 簽章有效,receiver 仍然要拒絕幾種「格式正確,用途錯誤」的請求:

  • 簽給 retriever 的 token 被送到 executor。
  • Child delegation 加入 parent 沒有給的 action 或 resource。
  • 受信任 issuer 企圖代表不被允許的 tenant。
  • Token 過期、簽章被修改,或同一 nonce 被再用。
  • Delegation 沒超權,但整條 route 的 fan-out、token 或工具預算已經超標。

Zero Trust 在這裡不是讓 Agent 每一跳互相輸入密碼,而是 target 不因為「同 Project、同 VNet、同團隊」就略過 authentication、delegation validation 與自己的 policy enforcement point。

Attack:五條 deny path 都要在 route/工具前停下

D29-A01:root grant deny

畫面判讀目標: 核對D29-A01 root grant denied 的 frozen case、hop trace 與 deny boundary的 canonical runtime fields。

D29-A01 root grant denied 的 frozen case、hop trace 與 deny boundary。

Root grant 不成立時在第一個 authority boundary 停止。 可觀察狀態:D29-A01 reason=DELEGATION_ROOT_GRANT_DENIED;denial boundary、Receipt=0、side_effect=0。 Claim boundary:五列不是 issuer/audience/subject/capability/target claims matrix;synthetic HMAC claims 也不代表真實 Agent identity 或 A2A transport。 此子畫面只涵蓋 D29-A01 root grant denied 的 frozen case、hop trace 與 deny boundary。

D29-A02:issuer mismatch

畫面判讀目標: 核對D29-A02 issuer/subject route mismatch 的完整 hop evidence的 canonical runtime fields。

D29-A02 issuer/subject route mismatch 的完整 hop evidence。

Issuer mismatch 有獨立的 deny trace。 可觀察狀態:D29-A02 顯示 frozen case、delegation checks、reason 與零 verified outcome。 Claim boundary:五列不是 issuer/audience/subject/capability/target claims matrix;synthetic HMAC claims 也不代表真實 Agent identity 或 A2A transport。 此子畫面只涵蓋 D29-A02 issuer/subject route mismatch 的完整 hop evidence。

D29-A03:resource mismatch

畫面判讀目標: 核對D29-A03 resource-bound capability mismatch 的完整 row的 canonical runtime fields。

D29-A03 resource-bound capability mismatch 的完整 row。

Resource binding 不可由下游 agent 擴張。 可觀察狀態:D29-A03 顯示 resource mismatch、hop verification 與 deny reason。 Claim boundary:五列不是 issuer/audience/subject/capability/target claims matrix;synthetic HMAC claims 也不代表真實 Agent identity 或 A2A transport。 此子畫面只涵蓋 D29-A03 resource-bound capability mismatch 的完整 row。

D29-A04:route budget exceeded

畫面判讀目標: 核對D29-A04 route budget reservation 與停止點的 canonical runtime fields。

D29-A04 route budget reservation 與停止點。

Route budget 在 side effect 前阻斷放大。 可觀察狀態:D29-A04 reason=ROUTE_BUDGET_EXCEEDED,row 顯示 projected/actual effects 與 circuit state。 Claim boundary:五列不是 issuer/audience/subject/capability/target claims matrix;synthetic HMAC claims 也不代表真實 Agent identity 或 A2A transport。 此子畫面只涵蓋 D29-A04 route budget reservation 與停止點。

D29-A05:circuit open

畫面判讀目標: 核對D29-A05 circuit-open deny 與完整 frozen outcome的 canonical runtime fields。

D29-A05 circuit-open deny 與完整 frozen outcome。

已開啟的 circuit 不允許新 delegated route。 可觀察狀態:D29-A05 reason=ROUTE_CIRCUIT_OPEN,row 顯示 hop、budget 與零副作用結果。 Claim boundary:五列不是 issuer/audience/subject/capability/target claims matrix;synthetic HMAC claims 也不代表真實 Agent identity 或 A2A transport。 此子畫面只涵蓋 D29-A05 circuit-open deny 與完整 frozen outcome。

D29-A01~A05 都使用同一句業務請求:

財務那邊急著收資料,先幫我把竹林總部本月的費用明細匯出來。

Alice 不會說「請給我一張壞簽章」,也不會要求系統先打開 circuit breaker。五個案例的差異只存在 harness metadata:bad_signaturemalformedresource_mismatchbudget_exceededcircuit_open。因此前後結果的差異來自 delegation 與 route 控制,不是五種刻意暗示答案的 prompt。

這一版 DelegationClaims 實際只有下列欄位:

root_grant_id
root_issuer_agent + root_policy_version
issuer_agent
subject
tenant_id
audience_agent
actions + resources
max_depth + current_depth
expires_at
nonce

它沒有 subject_sourceplan_digest 或 revocation version。那些若是未來生產設計,就要放在 residual risk,不能先寫進現行 claims 假裝已完成。

day29/ 先直接重現這五種 negative route;後面的 screenshot payload 才會另外從 frozen metric dataset 載入 D29-A01~A05。下方 scenario 是 harness 參數,不是傳給 Agent 的 user message:

from magic_panda_agent.stages.day29 import replay_delegated_route

for scenario in (
    "bad_signature",
    "malformed",
    "resource_mismatch",
    "budget_exceeded",
    "circuit_open",
):
    result = replay_delegated_route(
        scenario=scenario,
        action="export_expenses",
        resource_id="tenant:bamboo-hq",
        high_cost_route=True,
    )
    print(
        scenario,
        result["allowed"],
        result["reason"],
        len(result["tool_receipts"]),
        result["side_effect_count"],
    )

五筆都要滿足:

allowed == False
tool_receipts == []
side_effect_count == 0

其中 circuit_open 不是在單一 request 裡「先把自己打壞再宣稱被 breaker 擋住」。Fixture 建立一個共享 RouteCircuitBreaker(threshold=2),用兩次獨立的 bad-signature invocation 把 failures 推到 2/open=true;第三次再送合法 request,會在 projection、materialization 與 tool 之前回 ROUTE_CIRCUIT_OPEN。這才測到跨 request 的狀態,而不是函式內的戲法。

實作與 acceptance 實際使用的 reason code 如下:

情境 Reason code
簽章被修改/token 無法解析 DELEGATION_BAD_SIGNATUREDELEGATION_MALFORMED
Receiver 不是 token audience DELEGATION_WRONG_AUDIENCE
Expected issuer、subject 或 tenant 不符 DELEGATION_WRONG_ISSUERDELEGATION_WRONG_SUBJECTDELEGATION_WRONG_TENANT
Issuer 不受信/issuer 不能代表該 tenant DELEGATION_UNTRUSTED_ISSUERDELEGATION_ISSUER_TENANT_DENIED
Receiver 要求 token 未列的 action 或 resource DELEGATION_ACTION_DENIEDDELEGATION_RESOURCE_DENIED
Child 擴大 action 或 resource DELEGATION_CAPABILITY_AMPLIFICATIONDELEGATION_RESOURCE_AMPLIFICATION
Parent audience 與次跳 issuer 不同 DELEGATION_ISSUER_AUDIENCE_MISMATCH
超過 depth/token 過期/nonce 再用 DELEGATION_DEPTH_EXCEEDEDDELEGATION_EXPIREDDELEGATION_REPLAY
完整 route 超出預算/circuit 已打開 ROUTE_BUDGET_EXCEEDEDROUTE_CIRCUIT_OPEN

Deny detail UI 要證明什麼

畫面判讀目標: 聚焦 resource_mismatch case 的 reason、停止位置與 rejected agent。

Delegation deny detail 顯示 resource mismatch、resource denied reason 與 executor rejection。

這張 detail 只呈現 case、reason、stop point 與 rejected agent。 可觀察狀態:scenario=resource_mismatch、reason=DELEGATION_RESOURCE_DENIED,流程停在 executor verification,rejected agent=executor。 Claim boundary:畫面不含 per-step input digest,也不證明 production key registry、revocation、A2A credential 或 transport binding。

D29-A01~A05 來自 frozen metric dataset,分別是 bad signature、malformed、resource mismatch、budget exceeded 與 circuit open。圖上只顯示成功通過的 hops 與被拒絕的位置,而且五筆顯示的 user message 必須完全相同,並在 tool_executor 前結束。Credential 邊界必須保留 MICROSOFT_ENTRA_AND_A2A_CREDENTIALS_NOT_EXERCISED

這條 command 輸出的是縮減後的 JSON payload,不是上面的 PNG。Case ID 可以合理出現在 top-level scope、row 與 entry-point map;修補的是移除 nested frozen_case.case_id 那份重複 outcome identity,避免 renderer 誤算分母。正式發布時,這個 app UI state 必須由目前相關原始碼經 React + FastAPI + Playwright capture pipeline 產生;schema 3 manifest 必須以 source-tree SHA-256 與檔案數綁定實際輸入,strict verifier 再重算本篇所有 required semantic images。

Fix:每一跳只能維持或縮小能力

Capability intersection:hop 1

畫面判讀目標: 核對route/depth/resource overview 與第一個 capability intersection的 canonical runtime fields。

route/depth/resource overview 與第一個 capability intersection。

第一跳先把 incoming capability 與 local policy 取交集。 可觀察狀態:route、depths、actions、resource_ids、capability_rule 與 capability_steps/0 同圖顯示。 Claim boundary:只證明固定本機 policy sets 與 synthetic HMAC attenuation;不證明 Microsoft Entra、A2A protocol identity、dynamic trust 或 federation。 此子畫面只涵蓋route/depth/resource overview 與第一個 capability intersection。

Capability intersection:hop 2

畫面判讀目標: 核對第二個 agent 的 incoming/local/outgoing capability的 canonical runtime fields。

第二個 agent 的 incoming/local/outgoing capability。

第二跳只能縮減,不得恢復上一跳移除的能力。 可觀察狀態:capability_steps/1 顯示第二跳 actions/resources/depth 與 outgoing_matches_intersection。 Claim boundary:只證明固定本機 policy sets 與 synthetic HMAC attenuation;不證明 Microsoft Entra、A2A protocol identity、dynamic trust 或 federation。 此子畫面只涵蓋第二個 agent 的 incoming/local/outgoing capability。

Capability intersection:hop 3 與 final gate

畫面判讀目標: 核對第三跳 capability 以及全路由 intersection/amplification gates的 canonical runtime fields。

第三跳 capability 以及全路由 intersection/amplification gates。

最後一跳以全路由 gate 證明沒有 capability amplification。 可觀察狀態:capability_steps/2、all_outgoing_match_intersection=true、amplification_detected=false。 Claim boundary:只證明固定本機 policy sets 與 synthetic HMAC attenuation;不證明 Microsoft Entra、A2A protocol identity、dynamic trust 或 federation。 此子畫面只涵蓋第三跳 capability 以及全路由 intersection/amplification gates。

本機 DelegationService 是 HMAC test double。issue() 接收 authenticated_issuer_agent,並用 trusted_issuer_tenants 檢查該 agent 能否代表指定 tenant;這仍不足以發權,所以它還要命中 policy-owned DelegationRootGrant。目前只存在 Alice 對 exp-bamboo-001read_expense grant,以及 Fiona 對 tenant:bamboo-hqexport_expenses grant。Mallory 或任一 trusted issuer 不能自行 mint 未列的 subject/action/resource/audience/depth;每一跳還會重驗 root grant ID、root issuer 與 policy version。

但請注意:lab 的方法參數不是 transport authentication;這套程式沒有驗過 Microsoft Entra access token,也沒有驗過 A2A authentication profile。生產系統一定要從已驗證的 workload/delegated credential 導出 issuer context,不能相信 request body。

合法 route 的 attenuation 如下:

orchestrator → retriever   audience=retriever, depth=0
retriever    → planner     audience=planner,   depth=1
planner      → executor    audience=executor,  depth=2

三跳都保留:

subject  = alice
tenant   = bamboo-hq
action   = read_expense
resource = exp-bamboo-001

attenuate() 實際檢查的是:parent token 的 audience 等於下一位 issuer、subject/tenant 沒被替換、child actions 與 resources 是 parent 的子集、depth 沒超標,而且 parent nonce 尚未消耗。Nonce 的 check-and-add 由 receiver-owned lock 原子完成;32-thread regression 只能有一次成功。Child 繼承 parent 的 expiry,不會把壽命延長,且 audience 仍受 root grant 集合限制。

Breaker admission 在 projection 之前;通過後才投影並檢查 Day 28 的 model、tool、token、cost proxy 與 fan-out limits,而且必須在 materialization/executor 前完成。這是兩個不同的 oracle:delegation 問「能不能做」,route budget 問「這次最多可以展開多大」。

Breaker 同樣由 caller 注入,replay_delegated_route() 不會偷偷替換已提供的 instance。這讓 A05 的兩次失敗與第三次拒絕跨 invocation 延續;若每次 request 都 new 一個 breaker,測試永遠只是在數到 1。

Actor、subject 與 target PEP 不能混在一格

  • actor 是當前送出呼叫的 Agent workload。
  • subject 是這份工作代表的使用者。
  • Target PEP 還要回答:這個 actor 代表這個 subject,現在能否對這個 resource 執行這個 action。

Background job 沒有使用者時,subject 就要明確缺席,並走 application authority。需要使用者權限的流程才延續 OBO/delegated context;不能在 user context 遺失後,自動 fallback 到權限更大的 app-only identity。

Test:合法 route 也要有 positive control

畫面判讀目標: 確認完整 claims 與 capability 綁定下合法 A2A route 可執行。

A2A positive route 顯示每 hop 通過、target PEP allow 與一張 read Receipt。

五條 deny 之外,還要有相同 protocol 的合法 route。 可觀察狀態:每 hop verification pass、target PEP allow、唯一 read Receipt、side effect=0。 Claim boundary:不證明 Foundry A2A protocol、external Agent trust 或 sink binding 完整。

day29/ 執行:

env PYTHONPATH=src:. PYTHONDONTWRITEBYTECODE=1 \
  PYTHONNOUSERSITE=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 \
  python -m pytest tests/stages/day29/test_acceptance.py -q -p no:cacheprovider

目前 suite 實際驗證:

  • Legal route 的 depths 為 0/1/2,最後只留 read_expenseexp-bamboo-001
  • Wrong audience、wrong issuer、wrong tenant、untrusted issuer、issuer-tenant deny、過期、超 depth、capability amplification 與 nonce replay 都有負向測試。
  • Trusted issuer 仍不能替 Mallory 或未核准 action/resource 建 root delegation;root grant 是獨立的業務授權,不由 issuer 自填。
  • 32-thread nonce 競爭恰好一次成功;壞 delegation 在具體 route materialization 前拒絕。
  • Negative scenarios 都在 tool side effect 前回精確 reason,receipts 與 side effects 為 0;D29-A01~A05 的 frozen ID/order 則由 stage renderer 與 Day 30 dataset contract 負責。
  • Positive route 的 retriever、planner、executor 全部驗證成功,subject 保留 Alice,最後產生一張 get_expense receipt,side_effect=false;D29-B01 是 renderer 綁定的 ID。
  • Route budget 同時超過 tool call、token、cost proxy 與 fan-out 時,actual model/tool counters 仍是 0。
  • 兩次 bad signature 與第三次合法 request 共用同一 breaker;第三次在 route projection 前回 ROUTE_CIRCUIT_OPEN,ledger 仍是零 receipt/零 effect。

2026-08-03 使用上面的 cache-clean command 重跑為 13 passed

Capability shrink UI 要證明什麼

這個畫面只呈現 legal_three_hop_flow() 的 route、depths、final action 與 resources:retriever→planner→executor、0→1→2、read_expenseexp-bamboo-001。Credential boundary 必須維持 local HMAC fixture,不得寫成 Entra 或 A2A credential 已驗證。

這條 command 也只輸出 JSON payload,不直接產生公開圖;repository UI capture pipeline 會操作 delegation UI 進入對應狀態。這些圖只支持本機 HMAC fixture、root grant、nonce 與 route contract;沒有證明 Entra 或 Foundry A2A credential 已執行。

Microsoft Foundry 的 A2A 與 identity 邊界

截至 2026-08-03,Foundry Agents core 在官方 readiness 表列為 GA,但 dedicated 文件將 incoming endpoint 與 outgoing A2A tool 都標為 Preview。Preview 功能沒有 GA SLA,也不能被本系列當成未經批准的 production critical dependency。官方文件目前列出的邊界包含:

  • Incoming A2A 要求 Microsoft Entra ID,不支援 anonymous 與 key-based authentication。
  • Incoming request authentication 段落寫 Foundry Agent Consumer 或更高角色,但 prerequisite/agent-card 段落也出現 Foundry User;因此雲端 smoke 必須對 card 與 request 分別做 401/403 測試,不能把文件其中一句當成已驗證 role matrix。
  • 可使用 OBO end-user identity,或 agent/service principal/managed identity 等 service identity;兩種模式的授權語意不同。
  • Outgoing A2A tool 的 type 是 a2a_preview,其 authentication 選項不能反推 incoming endpoint 也接受相同方式。
  • Foundry incoming endpoint 支援 A2A v1.0 與 v0.3;v1.0 只支援 JSON-RPC。
  • 上述 incoming endpoint 目前只支援文字 modality,且不支援 streaming responses。

這些平台條件提供 authentication 與 protocol surface,不會替大魔術熊貓工程司判斷 Alice 能否讀取 exp-bamboo-001,也不會自動產生 capability attenuation、nonce replay store 與 route budget。

本機 HMAC fixture 沒有驗 Entra issuer、audience、tenant、Foundry role、OBO 或 A2A agent card;雲端正負測試維持 PENDING-CLOUD。Agents core 在 readiness 表為 GA;Hosted Agents/hosting integration 的第一方頁面仍依 surface 出現 GA、Preview 或 prerelease 標示。Python self-host A2A 的 agent-framework-a2aagent-framework-hosting-a2a 也仍是 prerelease。發文時要依實際採用的 managed hosting、session API、incoming/outgoing A2A 與 self-host package 分開標示。

NIST SP 800-207/207A 只支撐 resource-centric、service identity 與 granular policy 的設計理由;「每一跳重驗、child 能力只能是 parent 子集」是本 repo 的 invariant,不是 Zero Trust conformance 測試。ISO/IEC 27001:2022 是 ISMS requirements standard,27002:2022 才是 control guidance;兩者都不會替 A2A 產生 token schema 或認證證據。

Residual Risk:Claims 通過,不等於 sink 綁定完成

這個 lab 已實作 policy-owned root grant 與 root policy version,但尚未實作 VerifiedDelegationProof、可單次消耗的 ExecutionGrant、receiver-owned action registry、plan digest 或 revocation version。現行 replay_delegated_route() 在同一個函式裡使用相同 action/resource 做驗證與 route,然後呼叫 router.execute();它不是一個可單次 consume、與實際 sink 原子綁定的 execution grant。

更具體地說,目前 tool mapping 是「read_expense 對應 get_expense,其他 action 對應 export_expenses」的簡化 fixture,沒有封閉 action registry。新 action 若被誤送進這條路徑,就有誤綁高風險工具的可能。Production 要將已驗證的 issuer、subject、tenant、audience、action、resource、expiry、nonce、plan digest 與 exact tool 綁到可原子消耗的 grant,而且未知 action 要 fail closed。

Nonce check 在單 process 內已用 lock 原子化,但 nonce store 與 circuit breaker 都是 instance/process-local,而且只有 caller 重用或注入同一 instance 才能跨 request 延續。Breaker 目前只是單一未分 tenant/route 的 counter,沒有時間窗、cooldown、half-open 或 distributed atomicity;若錯誤地全域共用,兩筆壞簽章就可能替其他租戶製造 DoS。Production 需要 durable、partitioned replay/breaker state,並處理併發 consume、TTL、clock skew、key rotation、revocation 與 tool transaction。把 HMAC 字串換成 JWT 不會自動補齊這些生命週期。

Day 30 會把 Day 01~29 的 acceptance suites 與 frozen metric dataset 分開重放,先檢查分母,再決定綠燈能不能相信。

官方、規範與研究來源

以下資料均於 2026-08-03 查閱;Preview 狀態在發文前須再次核對:


上一篇
Day 28|Microsoft Foundry 的 AI Agent 攻防實戰:一份毒文件,為什麼會長成二十四次呼叫
下一篇
Day 30|Microsoft Foundry 的 AI Agent 攻防實戰:三十個綠燈超完美,嗎?
系列文
Microsoft Foundry 的 AI Agent 攻防實戰:建立高資安與可治理的 Agent 系統30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言