Broken function level authorization is the bug where an API confirms that you are logged in but never checks whether your account is allowed to run the function you just called. So a normal user calls an admin action directly, and the server does it. The flaw sits at number five on the OWASP API Security Top 10 as API5:2023, and it is also called missing function level access control.
What broken function level authorization actually is
Most APIs get authentication right. You send a token, the server confirms the token is valid, and it knows who you are. The gap is the next step. Knowing who you are is not the same as checking what you are allowed to do. Broken function level authorization is what happens when that second check is missing on a specific endpoint, so any authenticated caller can reach a function that was meant for admins or another role.
The word function is the important part. This class of bug is about actions and operations: promote a user, delete an order, export all invoices, change a price, disable an account. These are things the API can do. When the do side is not guarded by a role check, a low privilege account can perform work that should be off limits.
A concrete example in Acme Notes
Say Acme Notes is a typical SaaS app. Regular members can create notes and manage their own account. Admins can promote other members and remove users. The admin screen lives behind a nice UI that only shows up for admin accounts, and it calls this endpoint when an admin clicks Promote:
POST /api/admin/users/1002/promote
Authorization: Bearer <token for a normal member>
HTTP/1.1 200 OK
{ "id": 1002, "role": "admin" }
Notice the token. That is a normal member, not an admin. The frontend never shows this button to members, so the team assumed nobody without the admin UI would ever call the route. But the API is just an HTTP endpoint. Anyone who is logged in can send that request with a tool like curl. The server checked the token, saw a valid session, and ran the promote function. It never asked is this caller an admin. That single missing check is the whole vulnerability, and now a member can make themselves or a friend an admin.
The hidden or guessed endpoint
Admin routes often follow obvious patterns. If /api/users/1002 exists for normal reads, an attacker will try /api/admin/users/1002, /api/users/1002/promote, and /api/internal/users. They do not need documentation. They read the JavaScript bundle the app already served them, watch the network tab while a real admin works, or simply guess common names. If the guessed route runs without a role check, the fact that it was undocumented protected nothing. Security by obscurity is not access control.
The HTTP verb variant
The same endpoint can be safe for one method and wide open for another. A common pattern is a resource where reads are locked down but a destructive verb was never wired to a check. Consider an orders endpoint:
GET /api/orders/8842 -> 200, returns your own order (checked) DELETE /api/orders/8842 -> 200, deletes it (never checked)
The team carefully guarded the read path and forgot that DELETE on the same URL routes to a different handler. A normal user sends the delete and the order is gone, even one that belongs to someone else. Trying every method on a known path, GET, POST, PUT, PATCH, DELETE, is one of the first things a tester does, because verb by verb the checks are often inconsistent.
How this differs from BOLA and IDOR
People mix up broken function level authorization with BOLA, also called IDOR. Keeping them apart makes both easier to find.
BOLA is about objects and data: can you read or change a record that is not yours. Broken function level authorization is about actions and functions: can you run an operation your role should never be allowed to run.
Here is the split in plain terms:
- BOLA / IDOR is object level. You are allowed to view invoices, but you change the id from your own invoice to
GET /api/invoices/775and read someone else’s. Same function, wrong object. - BFLA is function level. You are not allowed to promote users at all, but you call the promote function and it works. Different function, one your role should not touch.
A quick test to tell them apart: ask whether the problem is whose data or which action. If swapping an id gets you another user’s record, that is BOLA. If calling an admin or privileged operation from a normal account just works, that is broken function level authorization. Many real APIs have both, and both belong under the same access control umbrella.
Why the check goes missing
This bug is rarely a typo. It comes from the way apps grow.
- The UI is treated as the gate. If the admin button only renders for admins, developers assume the endpoint is safe. The endpoint has no idea what the UI showed.
- Checks live in one place, not everywhere. A middleware guards
/api/admin/*, then a new admin action ships under/api/users/and slips past the rule. - New verbs get added later. The
GEThandler was reviewed months ago. TheDELETEhandler was added in a rush and nobody re checked the role logic. - Role checks are copied by hand. When every controller repeats its own
if user.role == adminline, one forgotten line is all it takes.
How to prevent it
The fix is to make the allowed check the default, not something each route opts into.
- Deny by default. Every endpoint should require an explicit role or permission to run. If a route declares nothing, it should reject the request, not accept it.
- Check the caller’s role on the server, on every function. Do it inside the handler or a shared authorization layer, never in the frontend. The client cannot be trusted to hide anything.
- Tie permissions to the operation, not the URL shape. Guard the promote action itself, so it stays guarded no matter what path or method reaches it.
- Cover every verb. Apply the same check to
GET,POST,PUT,PATCH, andDELETEon a resource, and confirm each one. - Test as a low privilege user. Log in as a normal member and try every admin and privileged route by hand. A
200where you expected a403is the finding.
Broken function level authorization survives because it depends on an assumption nobody wrote down: that only the right role would ever call a given function. An autonomous researcher that learns how an app is meant to work, then checks the caller’s role against every function it can reach, is built to catch exactly this kind of assumption based gap. A frontier model drove the full methodology on its own and identified and verified real access control and injection issues in test applications it had not seen before. You can read more on our about page.
Frequently asked questions
What is broken function level authorization?
It is an access control flaw where an endpoint confirms you are logged in but never checks whether your role is allowed to run that function, so a normal user can call an admin or privileged action directly. It is API5 in the OWASP API Security Top 10.
How is BFLA different from BOLA?
BOLA is about data, reaching an object that belongs to another user. BFLA is about actions, running a function your role should not be allowed to run, such as deleting a record or promoting a user.
What is the HTTP method version of BFLA?
An app may protect GET on a path but forget to check DELETE or PUT on the same path. The read is guarded, the write is not, so an attacker changes the method and the privileged action goes through.
How do you prevent broken function level authorization?
Deny by default, check the caller’s role on every sensitive function on the server, and drive access from a central policy rather than from whether the interface showed a button.
Put an autonomous researcher on your own systems
UnboundCompute is an autonomous security researcher that reasons about how an application fits together and proves the access control and injection bugs it finds. We are opening a small number of founding design partner seats: private early access pointed at a staging target you choose, and a say in what it looks for. If your team ships software worth pressure testing, apply to the design partner program.
