決定用哪一種機器憑證機制時,有一項成本在當下算不出來:選定的機制要什麼配套才跑得起來。那一項在選型會議上通常被算成零,而它一年後才到期。

選型要回答兩個問題,而它們的交集決定機制:這個秘密會不會在每次呼叫裡送出去、以及撤銷一次要影響幾個呼叫方。兩題之前還有一個前提要先確認——選擇權在誰。對方的 API 文件寫什麼就是什麼的整合,與自己定介面的整合,能做的判斷完全不同。

本章涵蓋與不涵蓋

本章聚焦系統層那一把憑證的機制選擇。呼叫方身分該不該分層、分幾層在 7.29 API 認證的信任邊界分層——那是本章的上游,機制選型只在確定系統層要有獨立身分之後才發生。選定之後憑證怎麼核發與交付在 7.32 機器憑證的配發,上線後的輪替與回收在 7.6 秘密管理與機器憑證治理。各機制的實作細節(雙密過渡怎麼做、mTLS 的 nginx 設定、簽章素材怎麼對齊)在對應的實作文章,本章給的是選哪一個與為什麼。

本章 threat scope

In-scope:秘密隨請求送出而落在日誌與中繼 / 撤銷粒度與呼叫方數量不匹配 / 機制要求的基礎建設沒有跟上 / 換發憑證的那一跳成為單點。

Out-of-scope(路由到他章):

  • 呼叫方身分要分幾層 → 7.29
  • 選定之後憑證怎麼交到對方手上 → 7.32
  • 上線後的輪替、回收與事件收斂 → 7.6
  • 這個機制屬於哪一類密碼學原語、金鑰放哪裡 → 7.28
  • 憑證的信任鏈與續期節奏 → 7.5
  • 一個系統代表某個特定的人 → 7.33

out-of-scope 的議題直接跳到對應章節。

從本章到實作

本章是 routing layer,沿兩條 chain 進入 implementation:

  • Mechanism:問題節點表「前置控制面」欄的連結進知識卡,看該控制的機制、邊界與適用條件。
  • Delivery:「交接路由」欄位指向 04 可觀測性05 部署平台06 可靠性08 事故處理。本章有一列走 04,因為「秘密落進紀錄」的偵測與遮罩落在那一層。

兩條 chain 完成判準與模組級 chain 規格見 從章節到實作的 chain

先確認自己有沒有選擇權

判別式是誰寫整合規格,不是誰是伺服器那一端。這兩者常常一致而不總是一致:接第三方的 webhook 時被呼叫的是自己的 API,而機制由對方的文件決定;提供平台給大量客戶接的一方即使一直在被呼叫,規格也是自己寫的。判錯這一題會讓後面兩問白做。

規格由對方寫時,能用的機制由對方提供。規格由自己寫時選擇權在自己,但每一個既有的呼叫方都要跟著改。既有介面換機制的邊界,是能不能同時接受新舊兩種機制並訂出落日期。 呼叫方兩百個一樣換得掉,只是要一年;做不到就只剩一次性切換,而那需要全體同時配合——這一支本站尚無專章,最小做法是把它當成一次對外的破壞性變更來排:先公告期限、期間新舊都收、到期日之後才關掉舊的,通用紀律見 11.6 向後相容的變更紀律

沒有選擇權時本章其餘各節仍然有用,但用途不同:它給的是「照對方的方式接上去之後,我這邊還缺哪一塊」。對方只給一把長期 API key 時,暴露面與撤銷粒度都由那個選擇決定,自己能做的是在自己這一側補上日誌遮罩與存取隔離,並把差額記成已知風險。對方要求用它自己的 SDK 時連這一步都做不到——機制被封裝、看不見也遮罩不了,能留下的只有「這條整合的暴露面由對方的實作決定」這句紀錄,以及把它排進定期重評估。

雙向整合的兩個方向各跑一次。 我打對方用一把、對方回呼我用另一把,這是接第三方服務的標準形態,而兩個方向的規格作者、暴露面與撤銷路徑各自獨立。下方的判讀流程與四格表都是單向的,兩個方向要各走一遍、得到兩組答案。

兩個問題把選項分開

先分開兩個詞:憑證是這條整合的身分整體,秘密是其中不能外流的那一部分——共享密鑰的密鑰本身、mTLS 的私鑰。撤銷處理的是憑證,送不送出去說的是秘密。(7.6 的類型分層用的是另一套切法,那裡的「部署憑證」是與 secret、token、key 並列的四類之一。)

問題一:這個秘密本身要不要在每次呼叫裡送出去。 這一題決定它最後會留在哪些地方。

送出去的機制(共享密鑰、API key、client credentials 換 token 的那一次)把秘密交給整條傳輸路徑上的每一跳處理,而那些跳點各自有自己的記錄行為——反向代理的存取日誌、內容傳遞網路的邊緣節點、應用層的請求追蹤、以及對方那一側的同一組系統。秘密沒有洩漏,只是被記錄了,而記錄的保存期限與可讀範圍由那些系統各自的設定決定。放進網址參數是這一格最嚴重的形態,因為多數伺服器的預設存取日誌格式會完整記錄請求行、含查詢字串;放進 header 好一些,但請求追蹤與錯誤回報工具可能會抓 header——這一項各家預設不同,要逐一翻設定。兩者都查得到:翻一次自己的日誌格式設定與追蹤工具的欄位設定就知道。

不送出去的機制(共享密鑰簽章、mTLS)只把秘密用來運算:網路上流的是簽章值或握手結果,秘密本身留在兩端的記憶體裡。代價是兩邊都要實作運算邏輯,而排錯比「比對一個字串」難得多——失敗時只會得到「對不起來」,看不出是哪一個欄位不一致。

下方表格用「送得起」指這一題的判定結果:那些跳點的紀錄保存期限與可讀範圍查得出來、而且查出來的答案可以接受。查不出來就當作送不起。

跳點全在自己掌控之內、日誌設定翻得到而且沒問題時,這一題五秒鐘就答完,決策整個落在第二題與基礎建設那一欄。純內網的服務之間互相呼叫多半是這種情形。

問題二:撤銷一把憑證要影響幾個呼叫方。 這一題決定事件當天的處置範圍。

共享密鑰是一把服務全部,撤銷等於中斷整條整合,這條約束的實際份量見 7.29 身分維度分層模型 —— 該節中段列出系統層的撤銷由哪些事件觸發、以及為什麼同一個「立刻撤銷」的需求在這一層要用天或週計算。API key 與 mTLS 憑證是一個呼叫方一把,撤銷影響單一對象——而 API key 的這個能力來自那張登記表,不是來自憑證本身。粒度細到什麼程度是設計選擇,不是機制給定的:共享密鑰也可以每個對象發一把,代價是要維護那張表,而那正是 API key 這個名字實際指的東西。

兩題交叉之後的落點

兩個問題各自有兩個答案,交叉出四格,每一格有對應的機制:

只有一個呼叫方有多個呼叫方
秘密送得起共享密鑰API key(要有登記表)
秘密送不起共享密鑰簽章每個呼叫方一把簽章密鑰,或 mTLS

右下那一格是實際上最常見的組合(同時接多個外部推送提供方、又不想讓密鑰進入日誌),而它的兩個選項成本差距在配套而非在密鑰數量:每方一把簽章密鑰要的是與 API key 同一張登記表,mTLS 要的是一整套憑證的簽發、續期與撤銷。前者是一張表、成本隨整合數變動,後者是一套基礎建設、多半是固定成本。所以整合數少時 mTLS 貴、共用同一個 CA 的整合夠多時成本結構會翻轉,攤提軸的算法見 7.32 的信任錨交叉點

下表展開這些機制各自的性質。四格與下表的列不是一對一——共享密鑰簽章那一列同時承擔左下與右下兩格,差別在一把共用還是每方一把;右下那一格的另一個選項才是 mTLS。

機制秘密要不要送出去撤銷一次影響幾個呼叫方要有的配套
共享密鑰共用該密鑰的全部
API key單一(靠登記表達成)憑證與呼叫方的登記表、發放介面
共享密鑰簽章不要共用該密鑰的全部;每方一把時為單一兩端的簽章實作與素材規格
mTLS不要單一CA、簽發與續期、撤銷清單或線上查詢

真正決定撤銷粒度的不是機制的名字,是有沒有一張把憑證對應到呼叫方的表。 共享密鑰與 API key 在密碼學上是同一件事——都是持有即通過、都做等值比對——差別只在那張表。有表,撤一把只影響一個;沒有,撤一把影響全部。名字本身靠不住:有服務把全帳號共用的靜態 token 叫 API key、也有服務按夥伴發放並登記卻叫它 shared secret。所以「我們改用 API key」這句話在團隊裡要講清楚實質內容是建那張表,否則會被聽成改個 header 名字。

這張表也可以不由自己維護:雲平台派發的身分(實例掛載的角色、容器編排的服務帳號)粒度同樣是單一,而對應關係在平台那一側,自己這邊看得到卻改不了。這條路徑不在上表裡,它的判讀走 7.10 Workload Identity 與聯邦信任邊界,而它能不能用是 7.32 的第一題

mTLS 的兩個補述

保護到哪裡結束。 mTLS 與簽章的私鑰或密鑰都留在本地(TLS 用私鑰簽握手訊息、HMAC 用密鑰算驗證值),但兩者保護到的位置不同:mTLS 的客戶端身分在 TLS 終止的那一點就結束,前面有負載平衡器或內容傳遞網路終止 TLS 時,身分要由終止點轉成 header 往後傳,於是問題變成「後端憑什麼信任那個 header」。簽章值則一路走到應用層,中途每一跳都改不了它。有終止點的架構要把這一跳的信任邊界一起設計,見 7.29 系統憑證下放客戶端

撤了之後多久真的擋得住。 這與撤銷粒度是兩個問題:粒度是單一呼叫方,而生效時間由驗證側的檢查方式決定——撤銷清單有快取與更新間隔,線上查詢在查不到時多數實作預設放行。把延遲壓到有界的做法是縮短憑證有效期,而那要求續期是自動的,回到最右欄那一格。

OAuth client credentials 是另一個層次

它是取得憑證的流程,不是第五種機制,而流程裡用哪一種方式向授權伺服器證明自己,才決定秘密送不送出去。用 client secret 時秘密要送、而且是每次換 token 都送一次:RFC 6749 §4.4.2 要求每次 token 請求都做 client 認證,§4.4.3 又說不該發 refresh token,所以 token 過期只能再送一次 secret 去換——暴露頻率因此等於實例數乘上一天的秒數再除以 token 有效期:token 一小時的單一實例一天走 24 趟,而水平擴展的每個實例各自換各自的,五十個實例就是一千兩百趟——規模放大的正是暴露量。

同一個流程換成用私鑰簽出來的斷言(RFC 7523)或 mTLS 做 client 認證(RFC 8705)時,秘密就不送出去了,這一格移到四格表的下半。標準本身留了這個空間:RFC 6749 §2.3 明寫授權伺服器可以接受任何滿足其安全需求的 client 認證方式。

它真正帶來的是另一項:長期憑證與每次呼叫實際使用的憑證被分開,於是可以對後者設短有效期與細範圍,範圍表達見 authorization scope。已換出的 token 要等它過期還是撤得掉,取決於發行方有沒有提供撤銷端點與驗證方有沒有回查——無狀態驗證且不回查時才是「只能等它過期」。代價是換發端點成為每次呼叫的前置依賴。

判讀流程

  1. 先問這條整合需不需要有一把要交出去的憑證。呼叫方能用執行環境本身證明身分時整套選型都不必進行,判讀走 7.32 的第一題7.10。這一題答是的機率隨平台能力上升,而它一旦成立,下面四步全部作廢。
  2. 再確認規格由誰寫。由對方寫時直接跳到最後的登記那一步。
  3. 再問這個秘密送不送得起:傳輸路徑上有幾跳會記錄請求、那些記錄的保存期限與可讀範圍是什麼。確認不了時預設它會被記錄,選不送出去的那一類。
  4. 接著數呼叫方。只有一個且短期內不會變時可以接受共用一把,並把「出現第二個呼叫方」寫成重新評估的觸發條件;兩個以上直接要一方一把的粒度。把這一題的答案與「秘密送不送得起」那一題交叉,落點見上方的四格表。
  5. 再確認選中的機制要的基礎建設現在就有。mTLS 要問憑證續期是不是自動的、換發流程要問 token 端點掛掉時呼叫方怎麼辦。沒有的話這一項的建置要與整合本身一起排,而不是排在它之後;排不進去時只剩兩條路——退回同一格裡成本較低的那個選項(右下那格退回每方一把簽章密鑰),或接受並記錄例外,例外的期限與重評估條件見下方風險邊界裡「對方只提供一種機制」那一條。
  6. 最後把選定的機制與上面各題的答案一起登記。自己核發憑證的接到 7.32 機器憑證的配發 的核發與交付,登記落在那一章定義的那份清單上。落點是 7.32 定義的那份清單,本章在那份清單上加一個欄位(機制)與一種列型態:沒有選擇權、也沒有自己發出憑證的整合同樣佔一列,機制欄寫「由對方決定」,另記差額風險與重評估觸發器。

問題節點(案例觸發式)

問題節點判讀訊號風險後果前置控制面交接路由
秘密隨請求落進紀錄秘密出現在網址參數,或請求追蹤抓完整 header秘密的可讀範圍等於各跳點紀錄的保存範圍secret-managementaudit-log04 + 06
撤銷粒度與呼叫方不匹配一把憑證服務兩個以上的呼叫方撤銷單一對象要中斷全部token-revocationcredential06 + 08
基礎建設沒有跟上選了 mTLS 而簽發與續期是手動同批簽發的憑證同時到期,整合同時中斷certificate-rotation-renewalacme-automation05 + 06
換發那一跳成為單點token 端點故障時呼叫方沒有降級路徑長期秘密仍然有效,而所有呼叫方同時失去存取fallbackcircuit-breaker06

問題節點出現在什麼樣的系統

上表的訊號要等機制上線才量得到。設計階段對照的是下面這幾種形態。

秘密隨請求落進紀錄出現在把它當成一般參數處理的整合。網址參數這一種有兩種來源、處置不同:自己這邊「先讓它通」的除錯殘留改得掉,而對方的 API 只收查詢字串、或對方的 webhook 主控台設不了 header 這一種改不掉,只剩在自己這一側遮罩日誌並記成已知風險;header 那一種則是被工具帶進來的——請求追蹤與錯誤回報服務的預設多半抓完整 header,而那個預設在導入時沒有人逐項看過。識別動作是拿一段時間的存取紀錄搜尋自己的憑證前綴。

撤銷粒度與呼叫方不匹配出現在憑證比呼叫方先存在的系統。第一條整合開了一把密鑰,第二條來的時候那把已經能用,於是沿用——這與 7.6 token 分域不足 是同一個機制在機制選型層的形態。識別特徵是問得出「這把憑證現在有誰在用」的人只有一位。

基礎建設沒有跟上出現在安全評審把機制選對、而維運能力沒有一起評估的組織。選 mTLS 的決定在設計階段做,憑證續期的痛在一年後才發生,兩者中間隔著一次交接。它還有一半不在自己這一側:續期要對方同時換,所以就算自己這邊自動化做好了,中斷時間仍由對方的排期決定。

換發那一跳成為單點出現在剛從長期憑證換到動態換發的系統。換過去的動機是壓縮暴露窗口,而那個動機不會帶出「這個端點掛掉會怎樣」這個問題。最小做法有三項、都在呼叫方那一側:把換到的憑證快取到接近過期而不是每次呼叫都換、在過期前留一段提前續期的窗口讓失敗有第二次機會、以及換發失敗時明確決定要擋下請求還是沿用尚未過期的那一張。三項合起來把換發端點從「每次呼叫的前置」降成「每個週期的前置」,降級策略的形態見 fallback,那一跳本身的可用性目標走 06 可靠性

基礎建設沒有跟上是這張表裡唯一在選型當下不留任何痕跡的一格。合規要求對外整合走 mTLS,團隊建了一個內部 CA、為當時的兩個對接方各簽了一張一年期憑證,上線順利。一年後兩張憑證在同一週到期,兩條整合同時斷,而那一週沒有任何人在等這件事發生。

沒有被提前發現,是因為憑證到期不產生漸進訊號:到期前一秒的連線與平常完全相同,監控盯的連線成功率在到期當下垂直落下、沒有前兆。「還剩多久到期」要主動去查才有,而查它的動作在自動續期尚未建立的環境裡沒有承載者——它不屬於任何一個既有的排程。

止血要重新簽發並在兩端同時換,而對方那一側要人工配合,於是中斷時間由對方的排期決定。真正的修法在選型當下:把續期自動化當成選 mTLS 的前置條件而非後續工作,路徑見 acme-automation7.5 傳輸信任與憑證生命週期

常見風險邊界

  • 秘密出現在網址參數時,代表它的可讀範圍已經無法界定——各跳點的紀錄保存期限不由自己決定,處置要連同輪替一起做,輪替本身的節奏走 7.6 秘密管理與機器憑證治理
  • 同一把憑證服務兩個以上的呼叫方,而其中任一個是外部組織時,撤銷粒度的缺口會在事件當天變成「要中斷幾條整合」這個沒有好答案的問題。
  • 選定的機制要求的基礎建設由人工承擔時,這條整合的可用性上限由那個人的排程決定,這超出工程修復能承擔的範圍。
  • 換發端點沒有獨立的可用性目標時,代表所有依賴它的整合共用一個沒有人負責的單點。
  • 對方只提供一種機制而那一種不符合自己的合規要求時,處置要往合約層或架構層走:要求對方支援、在中間加一層自己控制的代理、或接受並記錄例外,例外的期限與重評估條件見 7.14 資安治理例外與 Tripwire

案例觸發參考

下一步路由