Let me tell you about one of the sneakiest API vulnerabilities out there: Broken Function Level Authorization, or BFLA. This is officially listed as API5:2023 in the OWASP API Security Top 10, and I consider it one of the most damaging because it often lets a normal user act like an administrator — just by knowing the right URL.
What Is Broken Function Level Authorization?
Every API has different “functions” — some are meant for regular users, and some are meant only for admins or specific roles. Examples of admin-only functions:
- Deleting a user account
- Changing another user’s role
- Viewing internal analytics
- Approving or rejecting refunds
- Accessing an internal debug or export tool
BFLA happens when the API checks that you are logged in (authentication) but forgets to check whether you are allowed to use that specific function (authorization).
I like to explain the difference like this:
- Authentication = “Who are you?”
- Authorization = “What are you allowed to do?”
BFLA is when an API answers the first question but skips the second one for certain functions.
How This Is Different From Broken Object Level Authorization (BOLA)
People often confuse BFLA with BOLA (Broken Object Level Authorization), so let me clarify:
- BOLA is about data — can I access someone else’s object, like User B’s order, User B’s profile, or User B’s invoice, using my own valid session?
- BFLA is about functionality — can I call an action or endpoint that I should not have permission to call at all, like an admin-only “delete user” or “promote to admin” function?
Both are authorization failures, but BOLA is about whose data, and BFLA is about which actions.
How BFLA Happens in Real APIs
1. Hidden Admin Endpoints That “Rely on Obscurity”
Developers sometimes assume that if an endpoint isn’t shown in the regular app UI, no one will find it. But APIs are discoverable through browser dev tools, mobile app decompilation, JavaScript files, or simply guessing URL patterns like /api/admin/users.
2. Missing Role Checks in Code
The endpoint exists, authentication middleware confirms the user is logged in, but the actual authorization check (“is this user an admin?”) was never written into that specific controller or function.
3. Inconsistent Authorization Across HTTP Methods
An API might correctly protect GET /api/users/{id} but forget to protect DELETE /api/users/{id} — same resource, different HTTP verb, different (missing) protection.
4. Client-Side-Only Restrictions
The mobile app or website simply hides the admin button from regular users, but the backend endpoint itself has no server-side check. Anyone who calls the API directly (bypassing the UI) can trigger the admin action.
5. Role Confusion in Multi-Tenant Systems
In systems with multiple organizations or tenants, a user who is an admin within their own company might accidentally be able to call functions meant for a global super-admin, because the code checks “is admin” without checking “admin of what scope.”
6. Versioned or Legacy Endpoints
An old version of an endpoint (/api/v1/admin/...) might still be live and lack authorization checks that were added later to the newer version (/api/v2/admin/...).
A Realistic Example
Imagine an HR management API. A regular employee logs in and can call:
GET /api/v1/employees/me
This returns their own profile — totally fine. But the API also has:
PUT /api/v1/employees/{id}/salary
If the backend only checks “is this a valid logged-in user token?” and never checks “is this user in the HR/admin role?”, then any employee could send:
PUT /api/v1/employees/104/salary
{ "salary": 500000 }
…and give themselves — or anyone else — a raise, simply because the function-level check was missing.
Another example: a SaaS platform with:
POST /api/v1/admin/export-all-users
If this endpoint doesn’t verify the caller’s role server-side, a regular free-tier user could call it directly and download the entire user database.
The Many Forms BFLA Can Take
Let me expand this into the specific patterns I look for when testing:
Horizontal Function Escalation
A user with one type of role calls a function meant for a different type of role at the same “level” — for example, a support agent calling a billing team’s refund-approval endpoint.
Vertical Function Escalation
A low-privilege user (regular user) calls a function meant for a higher-privilege role (admin) — the classic and most dangerous form.
Method-Based Bypass
The GET request is protected, but POST, PUT, PATCH, or DELETE on the same route is not.
Legacy Endpoint Bypass
Old, undocumented, or “deprecated” endpoints still work but were never updated with the current authorization logic.
Parameter-Based Privilege Change
Some APIs let you pass a role or isAdmin parameter in the request body during profile updates. If the server blindly trusts this (this actually overlaps with Mass Assignment too), a user can just set "role": "admin" themselves.
UI-Hidden but API-Reachable Functions
Functions that exist in the backend and are reachable via API calls, but are simply not shown as buttons in the frontend for regular users.
How to Test for BFLA
Here is my step-by-step testing approach:
- Map out every function/endpoint in the API, including ones only visible to admin accounts, using tools like Burp Suite, Postman, or by inspecting mobile app traffic.
- Create two test accounts — one low-privilege (regular user) and one high-privilege (admin).
- Capture requests made by the admin account for admin-only functions.
- Replay those exact requests using the low-privilege user’s token/session instead of the admin’s.
- Check the response — if you get a
200 OKand the action actually happens (not just a fake success message), that’s BFLA. - Test every HTTP method on each endpoint (GET, POST, PUT, PATCH, DELETE), not just the one the UI uses.
- Try old API versions (
/v1/,/v2/) and any endpoints found in JavaScript files, mobile app strings, or API documentation/Swagger files. - Test cross-tenant access if the system has multiple organizations — can an admin from Company A affect Company B?
How to Fix Broken Function Level Authorization
1. Centralize Authorization Logic
Don’t scatter “if user.role == admin” checks across every controller. Use a centralized authorization layer or middleware (like a policy engine, RBAC library, or something like Open Policy Agent) so every endpoint automatically goes through the same check.
2. Deny by Default
Design your system so that access is denied unless explicitly granted, rather than allowed unless explicitly denied. New endpoints should be locked down by default.
3. Enforce Role Checks Server-Side, Always
Never trust the frontend to hide a button as your only line of defense. Every sensitive function must re-verify permissions on the backend, every single time it’s called.
4. Use Role-Based Access Control (RBAC) or Attribute-Based Access Control (ABAC)
- RBAC — permissions tied to roles (admin, editor, viewer).
- ABAC — permissions based on attributes (department, tenant, resource ownership), which is more flexible for complex multi-tenant systems.
5. Apply Authorization Checks Consistently Across All HTTP Methods
If GET /resource/{id} is protected, make sure PUT, PATCH, and DELETE on the same resource path go through identical checks.
6. Remove or Properly Secure Legacy Endpoints
Audit old API versions regularly. If they’re no longer needed, retire them. If they must stay, apply the same authorization standards as the current version.
7. Write Automated Authorization Tests
Add tests to your CI/CD pipeline that specifically try to access admin functions with a non-admin token and expect a 403 Forbidden. This catches regressions before they reach production.
8. Log and Monitor Privilege Escalation Attempts
Track failed authorization attempts (repeated 403s on sensitive endpoints) so your security team can spot exploitation attempts early.
Business Impact of BFLA
- Full account or data compromise — attackers can promote themselves to admin.
- Financial fraud — unauthorized refunds, discount codes, or salary changes.
- Regulatory violations — unauthorized access to sensitive data can breach GDPR, HIPAA, or other compliance frameworks.
- Total system takeover — in the worst cases, BFLA on a “create admin user” endpoint can hand an attacker full control of the platform.
Final Thoughts
BFLA is dangerous precisely because it’s invisible from the outside until someone actually tests it. A well-designed UI can completely hide the problem while the underlying API remains wide open. The fix isn’t complicated in theory — check permissions on every function, every time — but it requires discipline across the entire codebase, especially as APIs grow and more endpoints get added over time.