cover

盤點自己的雲端帳號時,我看到一個上傳用的程式身分,跑了兩個指令確認它的權限範圍——列使用者被拒、列運算資源被拒。結論很清楚:這是一個窄權限的程式身分,只能碰它該碰的那個儲存空間,沒問題。

後來從主控台的使用者列表看同一個身分,那一列長這樣:有主控台密碼、沒有兩階段驗證、所屬群組掛著整個物件儲存服務的完整權限。

也就是說,只要有人拿到那組密碼,就能用瀏覽器登入,把那朵雲上所有的儲存空間翻一遍。而我剛剛才在報告上寫了「沒問題」。

我沒有查錯,我是查了一條路,然後以為那是全部的路。

兩個平面:request 走的路,跟人去改設定的路

雲端上任何一個資源,都同時掛在兩條完全不同的路上。

資料平面是流量真正走的那條:使用者的 request 打進來,經過網域解析、邊緣節點、入口、你的程式,最後碰到資料庫或儲存空間。你畫架構圖畫的就是這條。

管理平面是另一條:有人打開瀏覽器,登入主控台,把某個設定改掉。這條路上沒有半個使用者的 request,但它能把資料平面上的每一層都改掉——包括把儲存空間改成公開、把防火牆規則刪掉、把你的權限收走。

這兩條路不會交會。 一個身分可以在資料平面上窄得像針孔,同時在管理平面上寬得像大門。我踩的就是這個。

順著架構圖盤點的方法,天生看不到管理平面——因為它不在任何一條 request 路徑上,也不夾在任何相鄰兩層之間。你不會「走到」它,只能另外想起它。

為什麼直接看 policy 不夠

會有人說:那我把每個身分掛的 policy 印出來看不就好了。

不夠,而且不夠的方式有三種。

權限可能不是直接掛的。 我那次的身分,policy 是掛在群組上,不是掛在身分上。只查身分直接附加的權限,那一格會是空的——看起來乾乾淨淨。

能不能登入不是 policy 的一部分。 主控台密碼是身分的一個屬性,不是一條權限規則。你把權限規則翻爛也翻不到它。同理,兩階段驗證有沒有開,也不在 policy 裡。

API 被拒不代表主控台被拒。 這是最反直覺的一點:我的兩個指令回權限被拒,是真的被拒,但那證明的只有「這組金鑰不能呼叫那兩個 API」。人拿密碼從瀏覽器進去,走的是另一套判定。

三種漏法的共同點是一樣的:我用一個平面的證據,去回答另一個平面的問題。

從主控台的使用者列表下手,不是從 CLI

實務上的解法很土:先看主控台的使用者列表,那張表一次給齊所有平面的欄位。

CLI 反而常常查不到——盤點用的身分權限通常不夠大,一列使用者就被拒,然後你只能記「沒權限」。

那張表要盯這幾欄:

欄位為什麼是這欄
上次活動多久沒動,判斷是不是遺留物
主控台存取最容易漏的一欄,它決定這個身分是不是也在管理平面上
兩階段驗證有主控台存取但沒開,是高風險,跟用不用無關
密碼期限 / 主控台上次登入分辨「設定上可以登入」跟「真的有人在登入」
存取金鑰 ID 與年齡靜態金鑰放多久沒換
所屬群組權限可能從這裡來,不看這欄會漏掉大部分

判定規則只有一條要記:有主控台存取而且沒有兩階段驗證,不論用不用都要處理。 這不是「遺留物」等級的事,是現在正開著的門。

真的要刪身分的話,順序不能反:先確認它管理的資源已經清掉,再刪身分。而且刪掉使用者不會刪掉它所屬的群組,群組要另外收。

雲 IAM 為什麼長成現在這樣

這套東西不是一開始就這麼複雜的,每一層都是前一代撞牆撞出來的。

最早只有靜態金鑰:一組 ID 加一組密鑰,塞進設定檔,程式拿去打 API。簡單、好懂,然後撞到兩件事——金鑰沒有到期時間,而且不會告訴你它現在在幾台機器上。外流了你不知道,換掉它要找出所有用到的地方。這不是理論風險,公開版控上一直有人在掃這種字串。

臨時憑證與 Assume Role 解的是「到期」那半。 身分不再直接持有長期密鑰,而是去換一組幾小時後就失效的臨時憑證。外流的代價從「永遠」變成「幾小時」。同時它帶出一個副作用:「誰能變成誰」變成一種要盤的關係——A 能不能取得 B 的身分,這條線本身就是權限。

但 Assume Role 沒解掉「第一把鑰匙從哪來」。你的持續整合流程要去換臨時憑證,還是得先有一組長期金鑰存在某個地方。繞了一圈,靜態金鑰只是搬家。

OIDC 聯邦解的是這一塊。 讓外部系統拿它自己簽發的身分憑證,直接去換雲端的臨時憑證——雲端信任的是那個簽發者,不是一組存在誰那裡的密鑰。到這裡,持續整合流程可以完全不持有任何長期金鑰。

那 OIDC 撞牆會撞在哪?撞在信任關係本身變成新的攻擊面。你信任的是「某個簽發者底下、符合某個條件的身分」,那個條件寫得太寬——例如只綁組織不綁專案——任何人在那個組織底下開一個專案,就能換到你的憑證。長期金鑰的問題是「東西外流」,聯邦的問題是「條件寫鬆」,後者更難發現,因為它沒有一個具體的東西可以去掃。

盤到「沒權限」時,不要寫成「沒有」

盤點表上有兩個狀態長得很像,意思剛好相反:

狀態意思判定依據
沒權限盲區——查不到,不代表沒問題指令回權限被拒
不適用確定沒有——架構上就沒這塊服務 API 從未啟用,或無實體資源且實測沒有回應

我那次盤點,一朵雲的半邊全是「沒權限」(用來盤點的身分權限太窄),另一朵雲的運算類是「不適用」(那些服務的 API 從頭到尾沒被啟用過)。

兩者混在一起,報告就看不出覆蓋率了——而一份看起來乾淨的報告,可能只是有一大塊根本沒掃到。

判定「不適用」最有力的一招是列出這個專案啟用過哪些服務:服務從未啟用,就代表架構上沒有那塊,這比「找不到資源」確定得多。找不到資源可能只是你沒權限看。

延伸閱讀


資料平面決定使用者能拿到什麼,管理平面決定明天這一切還算不算數。