iT邦幫忙

2026 iThome 鐵人賽

DAY 3
0
AI Security

AI Agent 憑什麼動手?30 天拆解 Agent Identity、Delegation 與 Authorization系列 第 3

Day 03|為什麼不能讓所有 Agent 共用一組 Service Account?

  • 分享至 

  • xImage
  •  

Core Question

多個 Agent 共用 credential 時,企業會失去哪一些隔離、歸責與撤銷能力?

今天的問題

假設公司裡有兩個 AI Agent:

  • customer-research-agent 依照業務需求查詢 CRM,只需要 customer.read
  • support-ops-agent 根據客服案件更新 ticket,需要 ticket.readticket.update

兩者都是常駐服務,都會自主選擇 Tool、組合參數並連續執行 Action。為了部署方便,團隊讓它們共用 svc-agent-prod,再把同一個 API Token 放進兩個 runtime。

從維運角度看,這只少管一組 credential;從 Tool 的角度看,卻只剩一個 caller:svc-agent-prod。Tool 不知道這次 ticket.update 是 Support Agent 規劃的,還是 Research Agent 誤選了 Tool;更不知道是哪個部署版本、哪個 runtime instance 使用了那把 Token。

今天要回答的不是「Service Account 安不安全」,而是更精確的問題:Service Account 所代表的 identity boundary,是否和 Agent 真正的執行、授權與撤銷邊界一致?

先把本篇的三個核心提問說清楚:

  1. 這個 Agent 是誰? Tool 至少應辨識實際 caller 所屬的 Agent workload;需要逐 instance 處置時,還要能關聯 runtime instance。
  2. 它現在代表誰? 本例先假設 Agent 使用自己的 workload authority;若代表 User,User Identity 應放在獨立的 Delegation Context,不能拿共用帳號代替。
  3. 它憑什麼執行 Action? 因為 PDP 根據可驗證的 Agent Identity、Action、Resource 與 context 作出決策,而不是因為 Agent 碰巧拿得到一把共用鑰匙。

為什麼這不是傳統 IAM 問題

一般後端服務也不該任意分享 privileged credential,這點並不新。Agent-specific 的差異,在於同一個 runtime 不是只執行開發者預先寫死的 API 路徑:Agent 會把自然語言目標轉成計畫,動態選 Tool、產生參數,還可能因 Tool 回傳結果繼續下一步。

因此,共用帳號壓平的不只是兩個 process,而是兩個具有不同目標、Tool 集合、模型/提示版本、資料範圍與行動風險的自主執行者。當 Research Agent 因錯誤 context 選到 ticket.update,安全控制不能把「它理論上不該選」當成 deny;Tool 只會根據可驗證的 caller 與 policy 判斷。若 caller 永遠都是 svc-agent-prod,PDP 就沒有足夠粒度區分兩個 Agent。

這也說明為什麼在 request header 多放一個 X-Agent-Name 不算修正。拿著共用 credential 的任何 process 都能自稱另一個 Agent;未被 issuer 或 runtime attestation 綁定的名稱只是 metadata,不是 identity evidence。

Threat / Failure Scenario

共用 Service Account 會同時造成四種結構性失敗。

1. 權限取聯集,Least Privilege 失去對象

共用 principal 若要讓兩個 Agent 都能工作,通常會拿到:

customer.read + ticket.read + ticket.update

這不是任何一個 Agent 的最小權限,而是整個 Agent pool 的權限聯集。Research Agent 一旦選錯 Tool、受到不可信內容影響,或程式遭入侵,就能使用原本只屬於 Support Agent 的 ticket.update。模型防護可以降低選錯機率,卻不能取代 Tool 端的 authorization。

2. Audit 有事件,卻沒有可驗證的執行者

Tool log 可能完整記下時間、Action 與參數,但 actor 永遠是 svc-agent-prod

{
  "actor": "svc-agent-prod",
  "action": "ticket.update",
  "resource": "TICKET-4821",
  "decision": "allow"
}

事後只能證明「持有這組 credential 的某個 process」呼叫過 Tool,不能從該憑證證明是哪個 Agent。應用程式自行補上的 agent_name 可以協助除錯,但若 caller 能任意填寫,就不能成為可靠的歸責依據。

獨立 identity 也不會自動帶來法律或密碼學意義上的 non-repudiation;它只是讓簽章、受保護的 audit trail、policy version 與 deployment evidence 有一個正確的 principal 可以綁定。共享 bearer secret 則從源頭就消除了這個區分。

3. 撤銷只能一起死,或一起繼續冒險

若發現 customer-research-agent:v17 行為異常,安全團隊面臨二選一:

  • 撤銷 svc-agent-prod,連健康的 Support Agent 一起中斷。
  • 保留 credential,讓可疑 Agent 繼續擁有整組聯集權限。

即使緊急輪替 Token,只要新 Token 又同時散布到所有 Agent,identity boundary 仍沒有改變。Rotation 解決的是 key continuity,不是 principal separation。

4. 一處外洩,整個 Agent Pool 都被冒充

RFC 6750 對 bearer token 的定義很直接:持有者不需要另行證明握有某把密鑰。當同一 Token 存在多個 image、環境變數、CI job 或 log path,任一處外洩都能冒充整個共用 principal。

對 Agent 而言,blast radius 不只涵蓋多個 API,也涵蓋多個 Agent 原本互相分離的行動空間。攻擊者不必控制每個 Agent;只要取得 Agent pool 的共用 authority 即可。

核心概念

Service Account 不是問題,粒度不相符才是

Service Account 可以是合理的 workload identity 載體。問題在於用同一個 principal 與同一份 credential代表安全屬性不同的 Agent。

Identity 粒度應對齊至少三種管理邊界:

邊界 要回答的問題 粒度不足的後果
Policy boundary 哪個 Agent 可對哪些 Resource 執行哪些 Action? 只能授予權限聯集
Revocation boundary 哪個 Agent、deployment 或 instance 要被單獨停用? 撤銷造成連帶中斷
Evidence boundary 事後要把 Action 關聯到哪個可驗證 executor? 只能追到共用帳號

若兩個 Agent 的 owner、目的、Tool、資料敏感度、部署生命週期或 risk tier 不同,通常就不應共用 principal。這不是要求「每個 request 永遠建立新帳號」,而是要求 identity boundary 不得比 policy 與 incident response 所需的邊界更粗。

Per-workload 與 per-instance 不必二選一

可以把 Agent Identity 拆成兩層:

  • Workload identity:穩定識別一類受控部署,例如 agent/customer-research;適合作為 policy principal。
  • Runtime instance identity / attribute:識別本次啟動的 instance、deployment revision 或 attestation evidence;適合追蹤、隔離與短效 credential lifecycle。

同一版本、同一 owner、同一 policy 的水平擴充 replicas,可以共享邏輯 workload identity,但仍應各自取得短效 key material,並保留 instance correlation。反過來說,把 Research Agent 和 Operations Agent 只因為「都叫 Agent」而放進同一 Service Account,等於把分類標籤誤當 security boundary。

SPIFFE 提供一種 vendor-neutral 的參考:SPIFFE ID 用來明確識別 workload,SVID 則是 workload 證明該 identity 的短效文件;SPIFFE Workload API 由本地 endpoint 識別 caller,再交付對應的 identity material。重點不是一定要部署 SPIRE,而是「runtime 被辨識後取得自己的短效 credential」,而非由所有 Agent 讀取同一個預先放置的 secret。

Authentication、Delegation、Authorization 要分開

獨立 Agent Identity 只解決「直接 caller 是誰」,不能單獨回答「代表誰」與「能做什麼」:

Agent Identity      = 直接執行者,例如 agent/customer-research
Delegation Context  = 若有代行,記錄 User、Task、purpose、期限與限制
Authorization       = 對這一次 Action + Resource + context 的決策

因此,避免共用 Service Account 不是要發一張「萬能 Agent Token」給每個 Agent。Token 仍需限制 audience、期限、Action 與 Resource;若使用 OAuth access token,RFC 9700 建議限制最低必要 privilege、綁定特定 audience,並在適合的場景使用 sender-constrained token 降低外洩後的重放風險。

Architecture Pattern

Naive / Unsafe Design

https://ithelp.ithome.com.tw/upload/images/20260908/20120151JXRz5IQxyn.png

下側架構把 Agent 名稱、執行目的與 runtime 差異留在 credential 之外。Gateway 驗到 Token 後只看見 svc-agent-prod,所以後面的 policy 再精細,也沒有可信的輸入可以區分 A1 與 A2。

Recommended Design

上側架構的設計重點如下:

  1. 每個安全屬性不同的 Agent workload 擁有可驗證的 identity。
  2. runtime 透過 platform evidence 或 workload attestation 向 issuer 取得短效 credential,不在 image 中內嵌長效 shared secret。
  3. credential 至少限制 issuer、audience 與有效期;需要更強 replay protection 時,可採用 mTLS-bound 或 DPoP 等 sender constraint。OAuth mTLS 的標準做法可參考 RFC 8705
  4. Tool Gateway 作為 PEP,先驗證 identity,再把 actor、delegated subject、Action、Resource、Task 與環境 context 交給 PDP。
  5. Tool 不接受繞過 Gateway 的直連,或在 Tool 端再次做 resource-level enforcement。

NIST SP 800-207A 將 cloud-native access control 的重點放在 application/service identity 與 API gateway、sidecar 等 enforcement infrastructure,而不只依賴網路位置。這與本篇的核心一致:Agent 所在的 subnet 或「內網服務」標籤,不能替代可驗證的 workload principal。

Trust Boundary

這個 flow 至少跨越四條 trust boundary:

  • Agent runtime → Identity Issuer:issuer 必須從可信 runtime evidence 辨識 caller,不能接受 Agent 自報名稱。
  • Agent → Tool Gateway:Gateway 驗證 credential 的 signature、issuer、audience、expiry,以及必要的 proof-of-possession。
  • Gateway → PDP:decision input 必須來自已驗證 claims 與受控 context;不可直接相信模型產生的 roleagent_idapproved=true
  • Gateway / Tool → Resource:只有 allow 的具體 Action 能跨界;Tool 仍需約束 target resource 與參數。

Identity Flow

一個最小 identity flow 可以是:

  1. customer-research-agent 啟動,向 issuer 提交可驗證的 runtime evidence。
  2. issuer 將 evidence 對應到 agent/customer-research,發出短效且 audience-bound 的 credential。
  3. Agent 請求 customer.read,並把 credential 交給 Tool Gateway。
  4. Gateway 驗證 credential,從受信任欄位取得 actor=agent/customer-research;若 Agent 代表 User,另行驗證 Delegation Context。
  5. PDP 評估 actor、subject、Action、Resource、Task、deployment 與 risk,回傳 allow、deny 或附帶 constraints 的 decision。
  6. Gateway 只轉送被允許的呼叫,並把 identity、decision、policy version 與 outcome 寫入同一條 evidence chain。

Authorization Decision Point

PDP 應收到結構化且可驗證的 input,而不是只看到「某個有效 Token」:

{
  "actor": {
    "workload_id": "agent/customer-research",
    "instance_id": "run-7f31",
    "deployment": "v17"
  },
  "delegation": null,
  "action": "customer.read",
  "resource": "crm/customer/1234",
  "context": {
    "task_id": "task-9ab2",
    "risk": "low"
  }
}

Policy 可以允許 Research Agent 查詢 CRM,拒絕它更新 ticket;Support Agent 則得到另一組規則。若 instance_id=run-7f31 被隔離,PDP 或 issuer 也能只停止該 instance,而不是撤銷整個 Agent pool。

要注意:每個 Agent 一個 Token仍不是終點。如果 Token 是永久 bearer secret、可被複製、沒有 audience restriction,或 Gateway 不做逐 Action authorization,blast radius 只是從「全部 Agent」縮成「單一 Agent」,並沒有消失。

小型 PoC

以下 Python 只用 standard library,而且刻意不用 JWT。它以記憶體中的 credential registry 模擬「Gateway 已驗證 Token 後得到的 principal」,只證明兩件事:audit attribution 的粒度,以及撤銷是否能對準單一 Agent。這不是 production authentication 實作。

shared = {
    "token-pool": {
        "principal": "svc-agent-prod",
        "allow": {"customer.read", "ticket.update"},
    }
}

isolated = {
    "token-research": {
        "principal": "agent/customer-research",
        "allow": {"customer.read"},
    },
    "token-support": {
        "principal": "agent/support-ops",
        "allow": {"ticket.update"},
    },
}


def call(registry, revoked, token, action, claimed_agent):
    credential = registry[token]
    principal = credential["principal"]  # Gateway 能驗證的 actor
    allowed = token not in revoked and action in credential["allow"]
    print({
        "actor": principal,
        "claimed_agent": claimed_agent,  # caller 可填,不能當 identity
        "action": action,
        "decision": "allow" if allowed else "deny",
    })


print("-- shared credential --")
call(shared, set(), "token-pool", "customer.read", "research")
call(shared, set(), "token-pool", "ticket.update", "support")
call(shared, set(), "token-pool", "ticket.update", "research")
call(shared, {"token-pool"}, "token-pool", "ticket.update", "support")

print("-- isolated credentials --")
call(isolated, set(), "token-research", "ticket.update", "research")
call(isolated, {"token-research"}, "token-support", "ticket.update", "support")

預期輸出重點如下:

-- shared credential --
{'actor': 'svc-agent-prod', ..., 'action': 'customer.read', 'decision': 'allow'}
{'actor': 'svc-agent-prod', ..., 'action': 'ticket.update', 'decision': 'allow'}
{'actor': 'svc-agent-prod', ..., 'action': 'ticket.update', 'decision': 'allow'}
{'actor': 'svc-agent-prod', ..., 'action': 'ticket.update', 'decision': 'deny'}
-- isolated credentials --
{'actor': 'agent/customer-research', ..., 'action': 'ticket.update', 'decision': 'deny'}
{'actor': 'agent/support-ops', ..., 'action': 'ticket.update', 'decision': 'allow'}

前三筆 shared log 的 claimed_agent 雖然不同,可驗證的 actor 卻完全相同;Research Agent 也吃到了權限聯集。第四筆撤銷 pool Token 後,Support Agent 一起失效。

isolated 模式下,Research Agent 的越權 Action 被拒絕;即使它自己的 credential 已撤銷,Support Agent 仍可繼續工作。這正是 policy boundary 與 revocation boundary 對齊後的差異。

真實系統還需要 signature validation、secure key storage、expiry、audience、rotation、replay protection 與受保護 audit storage;不要把範例中的字串 Token 延伸成自製 authentication protocol。

參考資料

今天得到什麼

  • 共用 Service Account 會把多個自主 Agent 壓成同一 principal,使 Tool 無法可靠區分真正的 executor。
  • 權限聯集會放大單一 Agent 選錯 Tool、遭入侵或 credential 外洩時的 blast radius。
  • Rotation 不等於 identity separation;能否單獨授權、撤銷與調查,取決於 principal 粒度是否對齊管理邊界。
  • 建議以 per-workload identity 作為 policy principal,配合 per-instance 短效 credential 與可信 runtime evidence。
  • Agent Identity 只回答直接 caller;User delegation 與逐 Action authorization 仍必須分開表達與驗證。

下一篇

把 Agent 拆成不同 identity 後,我們終於能回答「是哪個 Agent 動手」。但當 Agent 是受使用者要求執行任務時,只知道 executor 還不夠:Action 是使用者授意、Agent 自主推導,還是部署方政策允許的結果?

Day 04 將接著討論:Agent 代表使用者做事時,責任到底算誰的?


上一篇
Day 02|一個 Agent 到底算使用者、應用程式,還是 Workload?
下一篇
Day 04|Agent 代表使用者做事時,責任到底算誰的?
系列文
AI Agent 憑什麼動手?30 天拆解 Agent Identity、Delegation 與 Authorization6
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言