7.29 API 認證的信任邊界分層
本章的責任是把 API 認證拆成獨立的身分維度,讓每一層的憑證機制、洩漏後果與撤銷粒度各自清楚。
本章涵蓋與不涵蓋
本章聚焦身分維度的分層判讀與混層失效訊號。各層憑證的格式、儲存與生命週期實作屬於下游,案例用於檢驗分層在真實事件下是否維持區辨力。
本章 threat scope
In-scope:層級混用導致撤銷粒度失控 / 系統憑證下放到客戶端 / 跨系統身分對應的依賴順序未定義 / 第三方授權範圍過寬。
Out-of-scope(路由到他章):
- 人類身分的權限分級與 authorization → 7.2
- 機器憑證的輪替與收斂節奏 → 7.6
- 這個機制屬於哪一類密碼學原語、金鑰放哪裡 → 7.28
- 系統層那把憑證要用哪一種機制 → 7.34
- 跨系統工作負載之間的信任建立(workload federation) → 7.10
- 傳輸層信任 → 7.5
Reader 對 in-scope 列表的 specific threat 應該能反向 trace 到本章問題節點;out-of-scope 議題請直接跳到對應章節。
從本章到實作
本章是 routing layer,沿兩條 chain 進入 implementation:
兩條 chain 完成判準與模組級 chain 規格見 從章節到實作的 chain。
身分維度分層模型
分層的核心責任是讓每個身分問題有獨立的憑證與撤銷路徑。單一 API request 看似只問「這個呼叫合法嗎」,實際同時牽涉三個各自要回答、失效處置也各不相同的問題。三者並非同類物件:前兩個是有憑證、有撤銷路徑的信任邊界,第三個是掛在第一個之後的資料依賴,它的失效預設借用第一層的錯誤表面(下方「跨系統對應順序未定義」整節在講這件事)。
以一個具體請求看這三個問題怎麼同時出現:客服人員在內部工具點下「重寄這張發票」,內部工具因此呼叫計費系統的 API。計費系統收到請求後要分別回答——操作的人是誰、把請求送過來的是哪個系統、這個客服在計費系統這邊對應到哪個身分。三個答案來自三個不同的地方,任何一個答錯,後果與處置都不一樣。
- 使用者層:發起這個請求的人是誰。對應 Authentication 與可個別撤銷的 Token Revocation。
- 系統層:把這個請求送過來的系統是誰。對應共享密鑰或 Message Authentication、mTLS 等機器身分機制。
- 跨系統對應層:這個人在另一個系統有沒有對應身分。這一層是一套流程而非新的信任邊界,產出的是身分映射與其建立時機。
三層的關鍵差異在撤銷粒度,而這個差異要放在「什麼事件會逼出撤銷動作」底下才看得出份量。
使用者層的撤銷由個別的人的事件觸發:帳號被回報盜用、員工離職當天、使用者改完密碼要踢掉其他裝置、風控偵測到異地登入。這一層可以撤銷單一 session 而不影響他人,所以處置能立刻執行,不必知會任何人。
系統層的撤銷由憑證本身的事件觸發:密鑰被 commit 進公開 repo、知道那把密鑰的人離職、廠商通報他們那端外洩、輪替週期到期。這一層的憑證由所有呼叫共用,撤銷等於中斷整條整合——落到實際操作是先找到對方的窗口、約一個雙方都能配合的維護時間、兩邊同時換。同一個「立刻撤銷」的需求,在這一層要用天或週計算——前提是換發要對方改設定。對方能自助取得新憑證時這條約束不成立,而那由機制決定,選型見 7.34。
對應層沒有可撤銷的憑證,它的對應動作是清掉或修正映射,觸發事件是某個人在其中一邊被停用、或兩邊的身分對應錯位。處理的是資料一致性,不是信任撤回。
這三維針對的是呼叫鏈上的單一請求。裝置粒度(撤銷單一裝置而保留該使用者其他 session)屬使用者層的細分,租戶維度屬授權範圍、路由到 7.2。另有一種形態落在三維之外:委任型憑證由單一 token 同時承載代理方與被代理方(OAuth token exchange 的 actor 與 subject),它的撤銷粒度是「哪個系統代表哪個使用者」這個組合,三層各自的粒度都對不上它。
會需要這種設計的是「一個系統代表某個特定的人去做事」的場景:第三方應用代表使用者呼叫服務端 API(協作工具的 app 幫使用者發訊息)、支付服務商代表商戶呼叫下游、企業 SSO 的中介系統代表員工存取後端服務。它們的共同需求是責任要能追溯到組合本身——出事時要查的是「哪個系統代表哪個人做了這件事」,而且兩邊可能各自需要撤銷:那個人離職要撤他、那個應用出包要撤整條整合,兩件事不該綁在一起。用兩張分開的憑證(使用者 token 加系統密鑰)也能表達這件事,差別在委任型把關係寫進憑證本身、驗證方不必自己拼湊。
本章對這一格的結論是預設用兩張分開的憑證,理由是它沿用既有機制就能表達同一件事。什麼時候該換成委任型、換過去之後撤銷粒度要另外做什麼才成立、驗證方要檢查哪些欄位,判準與交換機制都在 7.33 委任型憑證——那一章是這個選型的落點,本條只到「先用兩張走完一輪」為止。代理這件事在授權層要配什麼(發起的理由、有效時窗、紀錄裡的雙重歸屬),以及多跳代理什麼時候可以開放,見 7.2 代理操作的授權邊界。
判讀流程
- 先拆出這個端點實際牽涉哪幾個身分維度。拆法是問三個問題:這個操作要不要記錄「是誰做的」(有就有使用者層)、請求會不會由另一個系統代發而非終端使用者直連(會就有系統層)、這個操作要不要動到另一個系統裡的某個人的資料(要就有對應層)。內部的定時批次作業通常只有系統層,使用者直連的公開 API 通常只有使用者層,三層齊備的多半是跨組織整合。
- 再確認每一層各自用什麼憑證,以及該憑證的撤銷會影響誰。
- 接著確認層與層之間沒有互相代理 —— 一層的憑證不應該能通過另一層的驗證。例外是憑證格式明確承載兩個主體、且兩個主體各自可獨立撤銷的委任型設計:它同時滿足兩層並非混層,因為身分關係寫在憑證裡而非靠推定。判別方式是問這張憑證被撤銷時,失效的是哪一個主體。
- 最後把跨系統對應的建立時機與依賴順序寫成文件,路由到部署面與事故處置。
這四步是針對單一端點的。要對整個系統跑一次盤點時,直接把下方問題節點表的四個判讀訊號當成欄位,對每個整合對象逐一問過去,答案就是這條整合的分層現況。
問題節點(案例觸發式)
| 問題節點 | 判讀訊號 | 風險後果 | 前置控制面 | 交接路由 |
|---|---|---|---|---|
| 使用者憑證代理系統層 | 用使用者 token 驗證呼叫方系統身分 | 撤銷單一使用者會中斷整條整合 | authentication、token-revocation | 06 + 08 |
| 系統憑證下放客戶端 | 共享密鑰隨前端或行動應用發佈 | 任何取得程式的人可冒充該系統 | secret-management、credential | 05 + 06 |
| 第三方授權範圍過寬 | 整合取得的權限超過該整合實際使用的範圍 | 第三方事件直接放大成內部資料暴露 | authorization、blast-radius | 08 |
| 跨系統對應順序未定義 | 身分映射的建立時機隱含在呼叫順序裡 | 首次呼叫失敗且錯誤訊息指向錯誤的層 | api-contract | 05 + 06 |
問題節點出現在什麼樣的系統
認出自己的系統屬於哪一類,是使用上表的前提。表格的「判讀訊號」欄要等設計已經落地才觀察得到,而設計階段還沒有訊號可看——這時能對照的是系統形態。
使用者憑證代理系統層的起點通常很自然:後端收到使用者的 token、接著還要呼叫另一個服務,於是把同一個 token 往下傳。微服務之間的 token passthrough、管理後台呼叫內部 API、或是「服務帳號」其實掛在某個真人的帳號底下,走的都是這條路。成因多半是這條整合原本只存在於內部、兩邊都是自己人,沒有人問過「呼叫方是誰」這個問題。
使用者憑證代理與跨系統對應順序的後果在這張表裡最不直觀,各寫一則;第三方授權範圍過寬的場面在下方的 GitHub OAuth 2022。它的失敗長這樣:一位工程師離職,交接時撤掉了他的帳號與 session,流程上沒有任何遺漏。幾天後某條跑了兩年的資料同步開始失敗,查了半天才發現它一直用著那個人的 token——當初測試時順手接上去、後來就沒有人動過它。這種整合的存在只寫在當事人的記憶裡,離職檢查表上不會有它,而它壞掉的時間點跟離職隔了幾天,第一時間沒有人把兩件事連起來。修起來要先找一個能代表這條整合的系統身分,而那正是當初省掉的那一步:要決定用哪種機器憑證與放在哪裡,走 7.34 機器憑證的機制選型;配發流程本身(這個交付動作能不能免掉、誰核發、走什麼審批、初次交付怎麼送到對方手上)見 7.32 機器憑證的配發。憑證上線之後的輪替與收斂節奏走 7.6 秘密管理與機器憑證治理。
系統憑證下放到客戶端出現在想省掉後端中繼的架構:行動應用直接呼叫第三方 API、前端直接打雲端儲存或地圖服務、IoT 裝置與桌面應用。動機通常是離線也要能運作、或少一跳延遲。這一類的識別特徵最明確——產出物裡有一把對方系統認得的憑證。
第三方授權範圍過寬的處境與前兩種不同,它常常不是自己這邊的設計選擇。任何裝了 SaaS 整合的系統都會碰到:CI 服務連程式碼倉庫、監控平台連雲端帳號、行銷工具連客戶關係系統。授權當下對方只提供粗粒度的 scope,想只給讀取、選項卻只有全部。
跨系統對應順序未定義要有兩邊各自的使用者體系才會發生。SSO 串接多條產品線、B2B 讓客戶的員工使用自家服務、收購之後的帳號整併都是這種形狀,識別特徵是同一個人在兩邊各有一個 ID,而某個地方存著這兩個 ID 的對應關係。
這一類的失敗長這樣:串接初期兩邊的帳號都是同一批人手動開的,映射就順手在第一次呼叫時建起來,沒有人覺得需要另外設計開通動作。等到客戶把自己的人事系統接進來,新進員工第一次登入就失敗,而系統回的是認證失敗——值班的人照認證的排查手冊查憑證、查時鐘、查有效期,全部正常,隔了幾輪才有人想到去看映射表。真正缺的是一筆對應資料,而那條排查路徑上沒有任何訊號指向資料。補起來要加的是明確的開通動作,那牽涉兩邊的流程協調,不是自己這端改程式碼就能收尾。開通動作要定義誰觸發、映射在哪個時間點寫入、找不到映射時回什麼錯誤,這三項見 API 認證的三層信任邊界 的「Layer 3:跨系統 Provisioning」一節。第四項「既有帳號怎麼回填」那一篇沒有涵蓋,而它正是本情境的入口(客戶把既有人事系統接進來時,兩邊都已經有一批帳號)——最小做法是把回填當成一次性的批次開通、走與日常開通相同的寫入路徑,讓映射的來源維持單一。
一個系統可以同時落在多類。跨組織的整合通常至少踩到第二類與第三類,而內部工具串接內部服務多半只有第一類。
跨章議題交叉引用
本章「第三方授權範圍過寬」是 7.2 供應商身分鏈傳導 在 API 整合層的展現。該議題的完整處理在 7.2,本條只補「授權當下的 scope 決定事件發生時的暴露面」這個前置訊號。
混層之後失去什麼
層級混用的代價不會在功能測試裡出現 —— 請求照常通過,問題在事件發生時才顯現。上一節列出的各類系統,各自失去的能力不同。
使用者憑證代理系統層失去的是撤銷粒度。系統之間的整合掛在某個使用者的 token 上,該使用者離職或密碼重設就會中斷整條整合;反過來要撤銷這條整合,只能停用那個使用者。兩個本來獨立的生命週期被綁在一起。
系統憑證下放到客戶端讓「呼叫方系統」這個身分本身失去意義。共享密鑰隨程式發佈之後,任何取得程式的人都能冒充該系統發出請求 —— 這與 7.28 金鑰位置決定對抗對象 是同一個機制問題在認證分層上的展現。
第三方授權範圍過寬的後果不由自己控制。整合方一旦出事,損失範圍由當初授予的 scope 決定,而不是由該整合實際用到的功能決定——兩者的差距就是白白多承擔的暴露面。
跨系統對應順序未定義時,代價落在排障路由上。身分映射若在首次呼叫時隱式建立,缺映射的錯誤會以認證失敗的形式出現,讓排查方向指向憑證而非資料。
第三方授權範圍與對應順序這兩個節點的判讀訊號需要主動收集才看得到。授權範圍是否過寬要比對授予的權限清單與實際呼叫過的端點,量法與顆粒由誰決定見 authorization scope。對應順序是否隱式,看的是身分映射的寫入點:映射由明確的開通動作(provisioning)建立時順序是定義好的,由業務端點在找不到映射時順帶建立則屬於隱式,判別方式是查該資料表的寫入來源有幾處。
GitHub OAuth 2022 展示授予範圍如何決定暴露面:整合持有的 token 權限過寬,第三方事件因此直接通往下游客戶的資產。Okta 與 Cloudflare 2023 展示同一層的另一條傳導路徑 —— 承載身分材料的是支援流程本身,收斂點落在資料分級與供應商事件觸發的輪替,而非授權當下給了多少權限。
以下基於通用工程知識補充:收斂 scope 的成本最低的時機是授權當下,之後的每一個窗口都要協調對方,各窗口的成本排序見 authorization scope 的設計責任段。本章要接住的是它在分層上的後果——第三方持有的那把憑證屬系統層,它的範圍決定了對方事件傳導進來的面積,而這一格的收斂與撤銷都由對方的排期決定。那時協調管道已經開著,順手把 scope 收斂進去不額外增加成本。
何時可以合併層級
分層的成本落在自己團隊的部分是憑證配發、輪替與各層監控,這些可以排進迭代。既有整合對象要改自己的程式碼才能配合新的分層,而那個排期不歸自己控制——時程由這一項決定,不由自己的實作工作量決定。估算補分層要多久時,基準是這一項而非自己的實作工作量 —— 這也是「先上線再收斂」在認證分層上很少發生的原因。規模與整合數量不足時,合併層級是合理的選擇,而「明確知道放棄了什麼」要落成三行字才算數:合併掉哪一層、因此失去哪種區辨力、以及下方哪一個訊號出現時要補回來。這三行寫進該服務的設計決策紀錄,否則做過與沒做過的產物完全相同,日後接手的人只能重做一次判斷。
單一前端搭配單一後端、沒有第三方整合時,系統層的獨立憑證帶來的區辨力有限 —— 呼叫方只有一個,「是誰呼叫的」這個問題沒有分歧。此時把驗證集中在使用者層可以接受,代價是日後新增第二個呼叫方時要補上分層。
跨系統對應層在雙方使用者體系一致(例如同一個 IdP)時可以省略。身分映射由 federation 承擔,路由到 7.10。
判斷是否該補回分層的訊號:出現第二個呼叫方系統、整合對象是外部組織、或撤銷單一憑證會影響到非預期的對象。
常見風險邊界
- 撤銷單一憑證必須先跟外部團隊協調排程時,事件處置的時間已經由對方的行事曆決定,這超出工程修復能承擔的範圍。
- 客戶端產出物中存在能通過系統層驗證的憑證時,代表該層的身分保證已經失效。要知道自己有沒有踩到,拿一份正式版本的產出物做字串掃描,看那些密鑰常數在不在裡面;行動應用要先反編譯再掃,前端則直接看打包後的檔案。掃到之後的止血路徑與一般的密鑰外洩不同:換掉密鑰會讓所有還沒更新到新版本的使用者一起失效,而行動應用的更新率不由自己控制,所以換 key 的動作要跟版本汰換排程綁在一起,不能當成一次性的輪替。順序是先在服務端加一層自己控制的中繼(讓客戶端改打自己的後端、由後端持有對方的憑證),中繼上線之後舊 key 才能安全作廢——這一步同時把金鑰從「隨客戶端發佈」移回「只在服務端」那一格,見 7.28 金鑰位置決定對抗對象。
- 第三方整合的權限範圍無法對應到它實際使用的端點時,代表暴露面超過需求。
- 同一個認證錯誤碼在兩個模組的處置手冊指向不同動作時,代表分層的語意沒有跨團隊對齊,排障路由會依接手的人而分岔。對齊的做法是讓錯誤回應自己帶出層別——每一層各有一組不重疊的錯誤碼,或在回應裡放一個欄位標明是哪一層拒絕的。做到這一步之後處置手冊不必互相協調,因為每個碼只對應一個層、也就只對應一條處置路徑。這個決定要在對外契約定案時做,見 api-contract;已上線的整合要改錯誤碼時,走 11.6 向後相容的變更紀律。
案例觸發參考
- 第三方 token 成為內部入口: GitHub OAuth 2022
- 支援系統身分鏈傳導: Okta 與 Cloudflare 2023
- token 洩漏後的存取延續: Slack 2022
- 授權範圍過寬的反例: 過寬的第三方 token 授權
下一步路由
- 使用者身分從哪裡來(在本章的上游):7.31 認證方式選型
- 從需求面回頭確認範圍:0.8 資安與資料保護需求 的「權限分級」與「密鑰與秘密」議題
- 憑證機制選型:7.28 密碼學原語選型
- 人類身分與權限分級:7.2 身分與授權邊界
- 呼叫方是瀏覽器裡的頁面時,憑證怎麼帶與隨之而來的跨站攻擊面:7.36 憑證在請求中怎麼帶
- 系統層那把憑證怎麼核發、初次交付與登記:7.32 機器憑證的配發
- 委任型憑證的交換流程、撤銷路徑與驗證欄位:7.33 委任型憑證
- 機器憑證生命週期:7.6 秘密管理與機器憑證治理
- 系統層確定要有獨立機器身分之後選哪一種機制(API key、共享密鑰簽章、mTLS、OAuth client credentials):7.34 機器憑證的機制選型。各機制的實作細節(雙密過渡、mTLS 部署、簽章實作)在 API 認證的三層信任邊界 的「Layer 2:系統層」一節與它列出的各篇。盤點與設計評審仍用本章的問題節點表
- 各層憑證在部署流程中的配置邊界:5.x 流量、配置與控制面邊界 的 Secret Boundary 段
- 事件發生後的止血與回復:8.x 止血與回復策略
- 撤銷路徑的演練設計:06 可靠性 目前沒有憑證撤銷演練的章節;在那之前,回退演練的通用形態見 6.x DR 與 rollback 演練