Broken Function Level Authorization (BFLA)
ID |
broken_function_level_authorization |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
medium |
Remediation Effort |
medium |
Family |
API5:2023 - Broken Function Level Authorization |
CWE |
CWE-285, CWE-269 |
Resource |
authorization |
Language |
any (model-level) + Java / C# (AST Phase C) + Python / JS / Go / PHP (regex Phase B) |
Description
Reports endpoints whose path or HTTP verb suggests an administrative or privileged function (/admin/, /management/, DELETE /users/{id}, POST /accounts/{id}/promote, …), and that require authentication, but show no model-level evidence of a role-bearing authorization check.
This is OWASP API5:2023 — Broken Function Level Authorization. Per-language source-walking lifts the verdict when no explicit role check (hasRole, [Authorize(Roles=…)], IsInRole, @PreAuthorize("hasRole('ADMIN')"), is_staff, request.user.is_admin, etc.) is found in the handler body.
Rationale
Authentication says the caller is some logged-in user. Function-level authorization is the additional check: is this user allowed to perform this operation? When the check is missing, any authenticated user can elevate their privileges by issuing requests against admin endpoints they discover (or learn from the documentation, or guess from the URL shape).
BFLA differs from BOLA in scope: BOLA leaks one user’s data to another user, while BFLA grants any user the capabilities of an admin. The latter is generally higher-impact — a single missing role check on DELETE /users/{id} lets any authenticated caller delete every user, terminate every session, or rotate every key.
Common failure modes:
-
Admin endpoints behind the same authentication middleware as user endpoints, with no further role gating.
-
@PreAuthorize("isAuthenticated()")(covers only authentication, not role). -
Role checks present on the controller’s user-facing methods but missing from a sibling admin method.
-
New endpoints added to an admin controller without inheriting the class-level role annotation.
Remediation
Add an explicit role-bearing check at the controller, method, or middleware layer. The check must mention roles or permissions, not just authentication.
-
Spring:
@PreAuthorize("hasRole('ADMIN')")or@PreAuthorize("hasAuthority('USER_DELETE')")on the method, or@Secured("ROLE_ADMIN"). -
ASP.NET Core:
[Authorize(Roles = "Admin")]or[Authorize(Policy = "RequireAdmin")]. -
JAX-RS:
@RolesAllowed("admin"). -
Express / NestJS: a
requireRole('admin')middleware or@Roles('admin')decorator + aRolesGuard. -
FastAPI:
Depends(require_role('admin')). -
Flask / DRF:
permission_classes = [IsAdminUser](DRF) or@admin_required(Flask). -
Laravel:
Route::middleware(['auth', 'role:admin'])(with Spatie) orGate::authorize('admin-only', $resource).
Separate admin and user paths under different URL prefixes when feasible — /admin/** for admin routes — and place a global role-gate at the routing layer so an individually missed annotation can’t expose the whole admin surface.
Configuration
The detector accepts a configurable allowlist of admin-shaped path patterns (adminPathPatterns) and privileged HTTP verbs — the built-in defaults cover the common conventions (/admin/, /management/, /internal/, /system/).
Unless your project uses non-standard admin path conventions, you typically do not need to configure this detector.
References
-
OWASP API Security Top 10 (2023) - API5:2023 - Broken Function Level Authorization.
-
CWE-285 : Improper Authorization.
-
CWE-269 : Improper Privilege Management.
-
OWASP Cheat Sheets Series: Authorization Cheat Sheet.
-
NIST SP 800-53 AC-6 : Least Privilege.