
盤點自己的雲端帳號時,我看到一個上傳用的程式身分,跑了兩個指令確認它的權限範圍——列使用者被拒、列運算資源被拒。結論很清楚:這是一個窄權限的程式身分,只能碰它該碰的那個儲存空間,沒問題。
後來從主控台的使用者列表看同一個身分,那一列長這樣:有主控台密碼、沒有兩階段驗證、所屬群組掛著整個物件儲存服務的完整權限。
也就是說,只要有人拿到那組密碼,就能用瀏覽器登入,把那朵雲上所有的儲存空間翻一遍。而我剛剛才在報告上寫了「沒問題」。
我沒有查錯,我是查了一條路,然後以為那是全部的路。
兩個平面: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 從頭到尾沒被啟用過)。
兩者混在一起,報告就看不出覆蓋率了——而一份看起來乾淨的報告,可能只是有一大塊根本沒掃到。
判定「不適用」最有力的一招是列出這個專案啟用過哪些服務:服務從未啟用,就代表架構上沒有那塊,這比「找不到資源」確定得多。找不到資源可能只是你沒權限看。
延伸閱讀
資料平面決定使用者能拿到什麼,管理平面決定明天這一切還算不算數。
