iT邦幫忙

2026 iThome 鐵人賽

DAY 16
1

💡 今日學習目標:理解 OWASP Top 10 第一名「權限控制失效 (Broken Access Control)」的攻擊手法(如 IDOR 越權存取),並掌握安全的權限驗證通則邏輯。


📌 前言:高居榜首的系統噩夢

A01 - 權限控制失效 (Broken Access Control) 是 OWASP Top 10 的衛冕冠軍:2021 年登上第一之後,2025 年依然穩坐榜首,而且守備範圍還變得更大了(Day 15 提過,SSRF 在 2025 年被併進了這一類)。

它的發生率是 3.74%,在十大類別裡數一數二。

什麼是權限控制失效?簡單說就是:使用者做到了他本來不該做到的事

而它有兩種常見的長相:

權限控制失效的兩種形態:平行越權(IDOR)與垂直越權(提權)

今天我們就把這兩種都親手拆一次。


🔍 形態一:平行越權(IDOR)

IDOR(Insecure Direct Object References,不安全的直接物件參考)通常發生在系統把資料庫的內部主鍵(如 order_id = 1001)直接暴露在 URL、表單或 API Body 中,而後端沒有驗證這筆資料到底屬於誰

攻擊成本低到誇張:把網址上的數字改一個就好。不需要工具、不需要技術,一個好奇的使用者就可能不小心發現。


🛠️ 實戰對比:錯誤邏輯 vs. 正確邏輯

❌ 不安全的處理邏輯(Vulnerable Pattern)

// ❌ 漏洞邏輯:只拿前端傳進來的 orderId 去查資料庫,完全相信前端輸入
Function GetOrderDetail(req):
    orderId = req.GetQueryParam("order_id")

    // 直接根據 orderId 搜尋訂單(未驗證該訂單擁有人是誰)
    Order = DB.Query("SELECT * FROM orders WHERE id = ?", orderId)

    If Order == Null:
        Return Error(404, "Order Not Found")

    // 🚨 直接傳回!攻擊者只要修改 order_id,就能看遍全世界的訂單
    Return JSONResponse(Order)

✅ 安全的處理邏輯(Secure Pattern)

要防範 IDOR,核心通則有兩種做法:

做法 A:在資料庫查詢中強行綁定當前 Session 的 User ID (Contextual Enforcement)

// ✅ 安全邏輯 A:強制使用伺服器端 Session 中的 User ID
Function GetOrderDetail(req):
    // 1. 從伺服器安全 Session/JWT 取得當前已驗證的使用者 ID (不可由前端篡改)
    currentUserId = req.Session.GetUserId()
    orderId = req.GetQueryParam("order_id")

    // 2. 雙條件查詢:訂單 ID + 擁有人 ID
    Order = DB.Query("SELECT * FROM orders WHERE id = ? AND owner_user_id = ?", orderId, currentUserId)

    If Order == Null:
        // 為了安全,不提示「權限不足」,統一提示「找無此資源」
        Return Error(404, "Order Not Found")

    Return JSONResponse(Order)

做法 B:套用通用權限檢查器 (Policy-Based Authorization)

// ✅ 安全邏輯 B:顯式執行 Policy 權限檢查
Function GetOrderDetail(req):
    currentUserId = req.Session.GetUserId()
    orderId = req.GetQueryParam("order_id")

    Order = DB.FindOrder(orderId)

    // 顯式調用權限原則 (Policy Check)
    If NOT AuthorizationPolicy.CanUserAccessOrder(currentUserId, Order):
        AuditLog.Warn("Privilege Escalation Attempt by User: " + currentUserId)
        Return Error(403, "Access Denied")

    Return JSONResponse(Order)

🤔 你可能注意到了:一個回 404、一個回 403,到底該用哪個?

這其實是刻意的差別,判斷標準是:你願不願意讓對方知道「這個東西存在」?

  • 回 404:適用於「連存在與否都不該讓他知道」的資源:別人的訂單、別人的檔案。回 403 等於親口告訴攻擊者「這筆訂單真的存在,只是不給你看」,他就可以拿來枚舉出所有有效的 ID。
  • 回 403:適用於「他知道存在、只是沒權限」的功能,例如後台頁面。這時回 404 反而會讓正常使用者一頭霧水。

做法 A 因為是查別人的資料,所以用 404;做法 B 如果套用在後台功能上,403 就很合適。重點不是哪個數字對,而是別在錯誤訊息裡送出你不打算給的資訊。


🔍 形態二:垂直越權(提權)

平行越權是「看到別人的資料」,垂直越權則更嚴重:一般使用者執行了管理員才能做的操作

它最常見的成因,是一句讓人背脊發涼的話:「反正前端不會顯示那顆按鈕。」

// ❌ 漏洞邏輯:後台 API 只靠「前端沒有畫出按鈕」來保護
Function DeleteUser(req):
    targetUserId = req.GetParam("user_id")
    DB.DeleteUser(targetUserId)      // 完全沒有檢查呼叫者是誰、有沒有資格
    Return Success()

⚠️ 攻擊者怎麼玩:他根本不需要你的前端。打開開發者工具看一眼網路請求、或直接猜 /api/admin/... 這種常見路徑,然後用 curl 送出去就結束了。前端的按鈕從來就不是權限控制(這正是 Day 14 說的「前端驗證不是防線」)。

// ✅ 安全邏輯:在伺服器端明確檢查角色,且預設拒絕
Function DeleteUser(req):
    currentUser = req.Session.GetUser()

    // 預設拒絕(SEI 法則 5):不是管理員,一律擋下
    If NOT currentUser.HasRole("Admin"):
        AuditLog.Warn("Privilege escalation attempt", user=currentUser.Id, action="DeleteUser")
        Return Error(403, "Forbidden")

    targetUserId = req.GetParam("user_id")
    DB.DeleteUser(targetUserId)
    Return Success()

🏗️ 但更好的做法是:不要在每支 API 裡各寫一次。

把角色檢查放進 Middleware / Filter,讓「所有 /api/admin/* 的路由預設都需要 Admin 角色」。這樣一來,就算未來有人新增了一支後台 API 卻忘了寫檢查,它也依然是被保護的。

這就是 Day 13 講的 SEI 法則 3「為安全政策設計架構」:讓權限檢查有一個統一實施的地方,而不是分散在幾十個函式裡等著被漏掉。


💡 一個常見的誤解:「改用 UUID 就安全了吧?」

很多人第一次聽到 IDOR,直覺反應是:把 order_id=1001 換成 UUID,攻擊者就猜不到了。

UUID 確實讓「亂猜」變得不可行,但它不是存取控制,只是把門牌號碼換難記一點而已

因為 UUID 還是會外流:

  • 它出現在網址列,使用者複製貼上分享就洩漏了
  • 它躺在瀏覽器歷史紀錄Referer 標頭
  • 它被寫進伺服器日誌分析工具客服工單
  • 它可能出現在某個列表 API 的回應中

而只要洩漏一次,沒有任何一層檢查會擋住他,因為你根本沒做檢查。

🛡️ 正確的定位:UUID 是很好的縱深防禦補強(Day 14 說的那疊防線之一),但它永遠不能取代擁有權驗證。用了 UUID,那句 WHERE owner_user_id = ? 還是得寫。


🎯 今日重點小結與防守心法

  • 🔹 心法 1:「身分」只能從伺服器端拿。前端傳來的 user_id 只是一個字串,它證明不了任何事。真正的身分來自無法被篡改的 Session 或已驗簽的 JWT。
  • 🔹 心法 2:每一次查詢都要問「這是他的嗎」。存取敏感資源時,查詢條件必須帶上擁有者:WHERE id = ? AND owner_user_id = ?。少了後半句,就是一個 IDOR。
  • 🔹 心法 3:把權限檢查放在架構裡,不要放在人的記性裡。用 Middleware 統一守住 /api/admin/*,比要求每個人「記得加檢查」可靠一百倍。

💬 明日預告:【Day 17】【動手做】OWASP A04 加密機制失效:敏感資料保護與密碼雜湊
明天我們要處理的是:當資料庫真的被拖走的那一天,你的使用者密碼還撐不撐得住。


上一篇
【Day 15】OWASP Top 10 縱覽:2021 到 2025 的排名大洗牌
系列文
槍林彈雨下的資安防守:從品質觀念切入,帶開發者從零動手作資安 30 天16
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
AndyAWD
iT邦新手 1 級 ‧ 2026-09-04 22:34:30

開始進入實作了嗎

我要留言

立即登入留言