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 + a RolesGuard.

  • FastAPI: Depends(require_role('admin')).

  • Flask / DRF: permission_classes = [IsAdminUser] (DRF) or @admin_required (Flask).

  • Laravel: Route::middleware(['auth', 'role:admin']) (with Spatie) or Gate::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