iT邦幫忙

0

[BFF_Security] Day 6:授權與金鑰的終局之戰

  • 分享至 

  • xImage
  •  

週五清晨,公司大樓的氣氛異常肅殺。
會議室門口站著兩位穿著深色西裝的外部專家——這是公司為了即將到來的上市 IPO,專門重金聘請的國際頂級紅隊資安審計顧問「黑狐」。

黑狐打開筆記型電腦,眼神銳利地掃過我們全體工程團隊:

「各位,恭喜你們熬過了微服務拆分與老舊系統的雙寫泥潭。
但在昨晚的滲透測試中,我發現了三個足以讓你們公司在上市首日就直接被監管機關勒令停牌的致命結構漏洞:

  1. 你們的 API 依然任由前端客戶端胡作非為:前端愛送什麼路徑、愛帶什麼參數,BFF 就老老實實幫忙轉發?
  2. 你們的 Token 存在毀滅性的橫向移動風險:BFF 手裡拿著一張全權限的萬用通行證,竟然大搖大擺地發給下游每一個微服務?!
  3. 你們的第三方金庫隨時可能引發監守自盜:後端代管了使用者的 Google Calendar 和 Stripe 金鑰,內部一個小小的背景排程竟然能拿到全量寫入權限?!
    如果今天下午五點前拿不出解決方案,審計報告上我只能蓋下『不合格』的紅章。」

小杰的冷汗當場浸透了後背。
資安長老張和架構師老陳相視一笑,老陳緩緩站起身,在白板上寫下了最後的三個絕殺模式:
「API Facade、RFC 8693 Token Exchange、以及 API Token Handler!」

https://ithelp.ithome.com.tw/upload/images/20261018/20104930RFVjO2JRxA.png


第一幕:API Facade——只准點菜單,不准進廚房自己開火!

「黑狐顧問,關於你的第一個質疑,」老陳在白板上畫了一個餐廳櫃檯:
「在過去的架構中,前端就像走進廚房自己開火的客人:想打什麼路徑、想傳什麼參數自己決定。但在現代 BFF 架構下,我們推行 API Facade(外觀模式):
前端唯一的權利,就是照著菜單上的代號點菜!」

https://ithelp.ithome.com.tw/upload/images/20261018/20104930kPuSSLwr2I.png

破解安全盲點:知道操作代號,絕不等於擁有執行權限!

黑狐嘴角泛起一絲冷笑:「老陳,少跟我玩文字遊戲。你們前端的 JavaScript 代碼是完全公開的,黑客隨便反編譯就能看到所有的 Operation ID(例如 admin.purgeUsers)。我只要在終端機送出這個 ID,你們不就乖乖執行了?」

「這就是剛入門工程師最常犯的低級錯誤!」老張接過話頭,直接打開代碼 day6/edge/facade.go:
「在我們的設計中,ID 只是個索引,真正的安全防線在於操作旁邊焊死的『角色守門員(Roles)』!」

// 詳見 day6/edge/facade.go
type Op struct {
	ID    string
	Vars  []VarSpec   // 參數的型別、數值範圍、Regex 格式全數寫死
	Roles []string    // 角色白名單:空代表一般登入者;非空則強制驗證特權角色!
	Run   func(ctx context.Context, id Ident, vars map[string]any) (any, error)
}

黑客就算偷看了前端代碼,知道有 admin.purgeUsers 這個 ID,但他只要沒有管理員角色,送進來直接被 403 當場擊斃,並在審計日誌留下 denied:role 的警報!


15 道鋼鐵安檢門與永久封閉(Seal)

在 day6/edge/facade_test.go 中,測試 TestFacade_RequestGatekeeping 嚴格檢驗了 15 種惡意與畸形請求:

https://ithelp.ithome.com.tw/upload/images/20261018/2010493002IQwKSpuF.png

兩項致命的工程細節:

  1. 註冊表在啟動時永久封閉(Seal()):嚴禁在執行期動態註冊新操作,杜絕任何人動態注入未經審查的端點。
  2. 操作參數嚴禁宣告使用者選擇器:參數名稱如果叫 user_id、customer、tenant,在註冊時直接 Panic 報錯!使用者的身分必須且只能來自安全 Session,前端完全無從代入他人 ID!

黑狐看著這份密不透風的檢驗清單,臉上的冷笑漸漸收斂,微微點了點頭。


第二幕:跨微服務橫向移動浩劫——萬用 Token 的覆滅

黑狐轉向他的第二個致命質疑:
「好,你們的入口大門守住了。那微服務之間呢?
你們 BFF 登入後拿到了一顆萬用 Access Token,受眾是 BFF 自己,裡面涵蓋了 orders:read、billing:read、hr:read 所有權限。
今天如果你們防護最脆弱的『推薦系統』被我拿下,我是不是就能拿著這同一顆 Token,橫向打穿帳務系統洗劫使用者的銀行存款?!」

https://ithelp.ithome.com.tw/upload/images/20261018/20104930m57ngiQmXc.png

小杰聽得背脊發涼。在微服務架構中,單一節點失守是常態,如果 Token 沒有做受眾與權限隔離,整個微服務叢集就是一座沒有防火門的紙牌屋!


解法:RFC 10017 §9.1 + RFC 8693 Token Exchange 窄化換發

老陳平靜地打開了 day6/edge/exchange.go:
「黑狐顧問,你說的情境在我們的系統中物理上不可能發生。因為我們的 BFF 根本不會把 Master Token 轉發給任何微服務!」

依據 RFC 10017 第 9.1 節(降低 Token 權限) 與 RFC 8693(OAuth 2.0 Token Exchange),BFF 在呼叫訂單微服務之前,會攜帶 Master Token 向授權伺服器(STS)進行縮小換發:

https://ithelp.ithome.com.tw/upload/images/20261018/20104930M0N6QgNS7J.png

三層銅牆鐵壁:

  1. 受眾隔離(Audience Isolation):訂單服務就算被攻破,黑客拿著這顆 Token 去打帳務服務,帳務服務一看 aud: https://orders.internal 與自己不符,當場 401 拒絕!
  2. 權限窄化(Scope Narrowing):換發出來的 Token 只有 read,黑客絕無可能拿去執行寫入。
  3. 主體與代理人稽核(act 宣告):Token 明確記錄 sub: ada 且 act: bff,微服務日誌清楚記錄「是 Ada 委託 BFF 代為呼叫」,責任歸屬一清二楚!

第三幕:第三方金庫——如何防範「監守自盜」?

黑狐拋出了他的最後一個殺手鐧:
「最後一個問題:你們 BFF 幫使用者代管了 Google Calendar 和 Stripe 的 OAuth Token。使用者當初為了在 UI 上修改行程,大方地授權了完整的『讀寫權限(calendar.events)』。
但你們系統內有一個每小時自動執行的『行事曆背景同步工作(Sync Job)』,它只負責拉取資料。如果這個同步工作被黑客注入了漏洞,它會不會拿著金庫裡的寫入鑰匙,把全公司的使用者行程全部刪光?!」

小杰愣住了:「這……這是在同一台伺服器裡的代碼啊!它要從資料庫拿金鑰,我們怎麼防它?」

「靠 API Token Handler 的三門交集原則(Three-Gate Least Privilege)!」老張敲響了桌面。

在 day6/edge/vault.go 中,我們實作了**「使用時最小權限(Least Privilege at Use-Time)」:
任何代碼想向金庫索取金鑰,必須同時通過三道門禁的
嚴格交集**:

https://ithelp.ithome.com.tw/upload/images/20261018/201049300mv1IVBvpp.png

即使使用者當初授權了完整的寫入權限,背景同步工作(job:calendar-sync)在物理上永遠只能拿到唯讀的 Token!


底層密碼學:AES-256-GCM 與 AAD 防搬移

第三方 Token 存入資料庫時,老張展示了底層加密的細節:

  1. 認證加密(AES-256-GCM):保證密文不可竄改,翻轉一個 bit 解密立即報錯。
  2. 隨機 Nonce:相同 Token 每次儲存產生完全不同的密文。
  3. 附加驗證資料(AAD 綁定):
    我們在 AAD 中強制綁定 UserID + Provider。
    防止內部惡意人員攻擊:即使有懷有惡意的資料庫管理員,把富豪 Ada 的加密 Token 複製貼上到貧民 Bob 的資料庫欄位中,Bob 在讀取時也會因為 AAD 不符而解密失敗!

避免提供者時鐘偏差引發的「刷新風暴(Thundering Herd)」

在測試過程中,我們還原了一個極其危險的真實故障:

如果 Google 或 Stripe 的伺服器發生時鐘偏差,回傳了一個『剛簽發就過期』的壞 Token,會發生什麼事?

在沒有保護的情況下,前端每個請求都會發現 Token 過期,並在同一秒瘋狂向第三方發起刷新請求,瞬間打爆對方的 API 配額並觸發全域封鎖!

我們在金庫中引入了 退避靜默期(Backoff Window):

  • 一旦刷新失敗(或回傳畸形過期 Token),金庫在 30 秒內直接退避拒絕,不再向第三方發送請求,徹底消除刷新風暴!

第四幕:全系列六天授權架構大終局流水線

至此,老陳在白板上將我們前六天所建造的所有安全防線,一筆畫串聯成一條完整的流水線:

https://ithelp.ithome.com.tw/upload/images/20261018/201049301NxZbT02O4.png

💡 老陳的總結金句:
「資安架構的最高境界:越便宜的檢查排在越前面,越昂貴的運算排在越後面!」
任何非法請求在最外層的 CSRF 或 Facade 檢查就會被就地擊斃,絕對不讓垃圾流量浪費下游 Token 換發與微服務資料庫的寶貴算力!


尾聲:審計報告上的綠色印章

下午四點五十分。
終端機裡跑完了專案目錄下的全部 32 個單元測試:

cd day6/edge && go test -race -v -count=1 ./...
# 32 項單元測試全數 PASS (含 Race Detector)!

黑狐默默合上筆電,站起身,向老陳、老張和小杰伸出了手:
「精彩。這是我近年來看過最兼具優雅架構與防禦深度的 BFF 實作。
Facade 限制了攻擊面、Token Exchange 斬斷了橫向移動、Token Handler 守住了第三方金庫。
這份 IPO 資安審計報告,我給你們評為『卓越(A+)』!」

會議室裡頓時爆發出一陣歡呼!小杰激動地跳了起來。

然而,老張卻依然冷靜地看著窗外的夕陽:
「各位,先別急著開香檳慶祝。
今天我們把權限和 Token 鎖得如同銅牆鐵壁。
但明天,當系統正式面向全球數百萬使用者開放時,我們將迎來最後一哩的極限考驗:

  • 當微服務不可避免地把 Token 寫進了 Log 或 APM 監控時,攻擊者撿到了 Token 真的還能用嗎?(RFC 9449 DPoP 的終極救贖)
  • 工程師在線上排查 500 Bug 時,如果不印 Token,到底該怎麼精準除錯?
  • 還有……當我們做完了這一切,BFF 到底還有什麼是『物理上永遠擋不住』的殘留極限?」

夕陽西下,但我們知道,最後一哩的終局防線,即將揭開序幕……

(未完待續,請見 Day 7:【大結局:凌晨四點的 Post-Mortem】最後一哩的縱深防禦——DPoP 護住哪一段、Token 怎麼不進 Log、BFF 擋不住什麼?)


📚 本回參考文獻


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言