API 中繼站設計流程
API 中繼站設計流程
概述
當客戶必須自行到第三方平台申請憑證、而這段申請流程反覆造成卡關時,用一個中繼層把授權、token 保管與 Webhook 轉發收攏起來,讓客戶端只保留最小責任。適用於任何「客戶自己申請 API、自己貼金鑰會卡關」的第三方服務:LINE、金流、物流、CRM。
第零步:先判斷該不該做
三個條件同時成立才值得做中繼站,缺一就先別動工:
- 同一段設定問題每月重複出現。 一次性的卡關靠文件就夠。
- 根因在第三方平台的申請流程,不在自己的程式。 若是自己的 UI 難用,改 UI 比蓋中繼站便宜太多。
- 客戶不要求平台帳號歸屬自己。 只要客戶堅持品牌帳號必須在自己名下,這套架構直接不適用。
另外必須先確認第三方平台的服務條款是否允許一組憑證服務多個獨立商家。合規風險大於技術風險,這一關過不了後面都不用做。
第一步:切信任邊界
決定三層憑證各自放哪裡(詳見 [[一對多信任邊界]]):
- 平台主憑證(如 Channel Secret):只放環境變數,連加密存都不存。
- 租戶衍生憑證(如
relay_secret):加密存資料庫,需要拿原文重簽 Webhook。 - 租戶識別憑證(如
site_key):加密存資料庫,登記時只回傳一次。
金鑰儲存判準:只需要驗證就存 Hash,需要拿原文出來用就可逆加密。 解密鑰匙與被加密資料分離存放。
第二步:設計持久層與快取層
持久層通常只需要兩張表:
- 租戶表:租戶識別碼、基址、回呼網址、兩把加密後的金鑰、狀態(pending / active / revoked)。
- 對照表:第三方使用者識別碼 → 租戶識別碼,用於 Webhook 事件路由。
快取層只放「短命、掉了也能重來」的資料,三類:一次性 nonce、token 的短期暫存、事件路由的熱路徑快取。最終一致性的儲存不能放「寫完馬上要讀到正確值」的資料。
第三步:鎖死 OAuth 四道防線
授權 callback 是無 nonce 的 top-level GET,四道缺一不可:
- state 自我驗證:
HMAC-SHA256(base64url(payload), 主金鑰)。簽在 base64url 字串而非原始 JSON,避免欄位順序造成驗簽失敗。payload 帶時間戳限時有效。用標準 HMAC 而非密鑰直接串接,可擋 length extension attack。 - nonce 一次性:發起時寫入快取,callback 消費即刪。這是補上時間戳防護必然留下的時間窗口,見 [[重送攻擊防禦]]。
- token 絕不進網址:改發一組短效 handoff code 放進導回網址,租戶後端再走 server-to-server 兌換真 token,重複兌換回 410。避免 token 留在瀏覽器歷史、伺服器 log 與 Referer。
- 常數時間比對:所有簽章比對走
timingSafeEqual,防時序攻擊。
另需驗證回呼網址與租戶基址同源,擋 open redirect。
第四步:設計 Webhook 分流
所有租戶共用一個對外接收端點,收件那一刻只做兩件事:驗簽、入列,然後立刻回 200,路由與轉發丟到背景,免得卡住平台的重送機制。
- 驗簽必須用未經解析的原始 bytes。先 parse 再 stringify 會讓欄位順序或空白改變,簽章直接對不上。
- 轉發時以該租戶專屬金鑰重新簽章,租戶端用自己那把驗。
- 做批次合併(例如 250 毫秒或累積 20 則先到先送),避免租戶端被高頻事件連環觸發。
- 每則事件帶原始事件 ID,租戶端據此去重。這是「至少一次」投遞的補償,不是「恰好一次」的保證。
第五步:誠實列出集中化的代價
上線前要明確告知,這三項是結構性代價而非實作瑕疵:
- 獨立失敗變共同失敗:中繼站掛掉全部租戶同時失效。
- 平台配額共用:租戶尖峰時互相排擠,這條通常先於技術瓶頸爆掉。
- 責任轉移:租戶零設定,等於設定、合規與配額管理全部收到營運方身上。
適用場景
- 第三方登入與推播整合(LINE、Facebook、Google)
- 金流/物流服務商的多商家代收與回呼分流
- 任何「租戶各自申請憑證會卡關」的 SaaS 整合層
限制
- 有規模甜蜜區:租戶太少是過度工程,太多則配額先爆。
- 不含權限分級(RBAC),一組租戶金鑰等於該租戶全部能力。
- 憑證只回傳一次擋不住人為外洩,管理仍靠人。
- 若部署在無訊息佇列的環境,背景轉發失敗沒有內建重試保證。
關聯概念
- [[一對多信任邊界]]
- [[Gateway 架構]]
- [[三層 API 驗證]]
- [[請求簽章]]
- [[重送攻擊防禦]]
- [[Webhook 整合]]