Skip to content
pwnsy
web-securityintermediate#api-security#bola#authorization#owasp#appsec

API Abuse Explained: BOLA, BFLA, and Excessive Data Exposure

How API abuse works through the OWASP API Top 10: broken object and function level authorization, excessive data exposure, and rate-limit defense.

APIs are where the data actually lives, and attackers have noticed. A web page can hide a button, but the API behind it answers to anyone who sends the right request. When an API authenticates a user correctly and then fails to check what that user is allowed to do, the result is API abuse: reading other people's records, calling actions meant for admins, or pulling far more data than a feature ever displays.

This guide walks through the abuse patterns that dominate the OWASP API Security Top 10: broken object level authorization, broken function level authorization, and excessive data exposure, plus the rate-limiting layer that limits the damage. For the defensive foundation, pair it with the API Security Guide.

Authentication is not authorization

The single idea behind almost every entry below is the gap between two questions. Authentication asks "who are you", and most APIs answer it well with tokens and sessions. Authorization asks "are you allowed to do this, to this specific thing", and that is where APIs fall down.

The reason is structural. A REST or GraphQL API exposes objects by identifier and actions by endpoint. The identifier and the endpoint are right there in the request, fully under the caller's control. If the server checks the token and then trusts the identifier, an authenticated user can reach past their own data by editing what they send. Exploiting a public API this way maps to Exploit Public-Facing Application (T1190) on MITRE ATT&CK.

Broken Object Level Authorization (BOLA)

BOLA is the number one API risk, and it is the simplest to understand. An endpoint takes an object identifier and returns or modifies that object without confirming the object belongs to the caller.

GET /api/v1/orders/1024   ->   your order
GET /api/v1/orders/1025   ->   someone else's order

If the server returns order 1025 to a user who owns only 1024, the authorization check is missing. The attacker simply increments, decrements, or guesses identifiers and harvests records one by one. Sequential integer IDs make this trivial, but even random identifiers are not a fix, because an attacker who obtains a valid ID from a referral link, a log, or a shared URL can still access it if the ownership check is absent.

This is the same underlying flaw as Insecure Direct Object Reference, viewed through the API lens. The defense is to check, on every request, that the authenticated user is authorized for the specific object named, using the user identity from the session rather than anything the client supplied.

Unguessable IDs are not access control

Switching from sequential integers to random identifiers raises the effort of enumeration; it does not authorize anything. A random ID that leaks in a log, a shared link, or a referrer is just as usable to an attacker as a guessed one. The only real fix is a server-side ownership check on every object access.

Broken Function Level Authorization (BFLA)

Where BOLA is about the wrong object, BFLA is about the wrong action. Many APIs expose privileged functions, deleting any user, changing a role, viewing all records, and rely on the client to only show those functions to admins. The endpoints themselves accept calls from anyone authenticated.

POST /api/v1/admin/users/1025/promote
DELETE /api/v1/admin/users/1025

A normal user who discovers or guesses these routes, often by reading the front-end code or changing an HTTP method, can invoke administrative actions directly. Common shapes of this bug:

  • Admin routes that check that you are logged in but not that you are an admin.
  • Endpoints where changing GET to POST or DELETE reaches an unprotected handler.
  • Role escalation through a mutation the UI never exposes but the API still honors.

The fix is to enforce role and permission checks at the function itself, server-side, denying by default, so reaching the route without the right role fails regardless of how the caller found it.

Excessive data exposure

This one hides in plain sight. An API returns a full object, and the front end displays only part of it. The rest, an email, a password hash, an internal flag, a full address, is sitting in the response body, one browser dev-tools panel away.

The root cause is returning the whole database record and trusting the client to hide fields. The client is not a security boundary. An attacker reads the raw response and takes everything in it. When paired with a BOLA gap, excessive exposure turns a single leaked record into a rich profile, and repeated across enumerated IDs it becomes a bulk breach.

The related failure is mass assignment, where an API binds a whole request body onto an object and lets a caller set fields they should never control, such as isAdmin or balance. See Mass Assignment Explained for that direction of the same trust mistake.

The defense is to shape responses and requests explicitly: return only the fields a caller needs, filter server-side, and bind only the fields a caller is allowed to set.

Missing rate limits and resource controls

Even a well-authorized API is abusable if a caller can hammer it without limit. Missing rate limits enable credential stuffing against login, enumeration against any ID-based endpoint, scraping of catalogs, and resource exhaustion that degrades the service for everyone. Rate limiting does not fix an authorization gap, and it does contain the blast radius of one while you close it, and it stops the abuse that needs no gap at all. See Rate Limiting Explained for the mechanics.

How the pieces chain together

These flaws are dangerous alone and worse in combination. A real attack usually strings several together. An attacker starts with a shadow endpoint nobody remembered to protect, finds it authenticates but does not authorize, swaps an object ID to reach another user's record (BOLA), reads fields the UI never shows in the raw response (excessive exposure), and, with no rate limit in the way, walks every ID in sequence until they have the whole table. No single bug in that chain looks catastrophic in isolation. Together they are a bulk breach.

This is why defending an API is a matter of applying the same few checks everywhere rather than hardening one dramatic endpoint. The attacker looks for the one route where the habit slipped.

Business logic abuse

Not every API abuse is a missing check on data. Some target the logic itself: replaying a one-time discount code, requesting a negative quantity to invert a charge, racing two withdrawal requests so both succeed against the same balance, or skipping a required step in a multi-stage flow by calling the final endpoint directly. Automated scanners rarely catch these because every individual request is well-formed and authorized. Finding them takes understanding what the workflow is supposed to guarantee and testing whether the API enforces that order and those invariants server-side.

Attack summary

AbuseOWASP API themeWhat the attacker doesPrimary defense
BOLABroken Object Level AuthorizationSwaps object IDs to reach others' dataPer-object ownership check on every request
BFLABroken Function Level AuthorizationCalls admin endpoints directlyRole check at the function, deny by default
Excessive data exposureBroken Object Property Level AuthorizationReads hidden fields in the raw responseReturn only needed fields, filter server-side
Mass assignmentBroken Object Property Level AuthorizationSets fields they should not controlBind only allowed fields
No rate limitingUnrestricted Resource ConsumptionEnumerates, stuffs, scrapes at scaleRate limits and quotas

How to defend against API abuse

  1. Authorize every object access. On each request, confirm the authenticated user owns or may access the specific object named, using the identity from the session, never an identifier from the request body.
  2. Enforce function-level roles server-side. Guard every privileged endpoint with a role and permission check, deny by default, and assume attackers will find and call hidden routes directly.
  3. Shape data explicitly. Return only the fields a response needs and bind only the fields a request may set. Do not send full records and trust the client to hide the rest.
  4. Rate limit and set quotas. Throttle by user and by endpoint, cap expensive operations, and slow enumeration and brute force even where authorization holds.
  5. Keep an inventory of endpoints. Shadow, deprecated, and undocumented endpoints are a favorite target. You cannot protect what you have not catalogued.
  6. Test with a second account. The fastest way to find BOLA and BFLA is to take an object or action that belongs to user A and try it as user B. Do this in review and in automated tests.
See which API flaws are being exploited now

Authorization flaws in public APIs are a leading cause of modern data breaches. Our Exploit Intelligence dashboard tracks live vulnerabilities across API and application components, so you can prioritize the authorization and exposure fixes attackers are actually using.

A worked example of a BOLA discovery

It helps to see how a tester turns a hunch into a confirmed finding, because the same steps are what an attacker runs. Suppose an app has a profile page that loads your details. Watching the traffic, you notice the page calls an endpoint that names your account by a numeric identifier. The response carries your email, your phone number, and your saved address.

The first move is to change the identifier by one and resend the request with your own valid session token attached. If the server returns a different person's profile, the ownership check is missing and you have a BOLA. If it returns an error, you are not done, because a single failure does not prove the check is sound. Try an identifier far from your own, try one that belongs to an account you control from a second login, and try the same request with a different HTTP method in case a sibling handler is less careful. The goal is to separate two cases: a server that genuinely checks ownership on every path, and a server that happens to reject one shape while accepting another.

Once a single swap works, the severity comes from scale. If the identifiers are sequential and there is no rate limit, a short script walks the whole range and pulls every record. If they are random, the attacker looks for places those identifiers leak, a shared link, a referral parameter, a log, a cached response, since a random identifier is only a speed bump, never an authorization decision. The finding that matters to a defender is the same one either way: the endpoint authenticates the caller and never confirms the caller owns the object it returns.

API abuse in GraphQL

The examples so far use REST paths, and the same failures appear in GraphQL with a different surface. A GraphQL API exposes a single endpoint and a typed schema, so the identifier and the action move into the query body rather than the URL. BOLA shows up when a query asks for a node by id and the resolver returns it without checking the caller owns it. BFLA shows up when a mutation performs a privileged action and the resolver trusts that the client would only send it for an admin.

GraphQL adds two wrinkles worth naming. The first is that a single query can traverse relationships, so a caller who reaches one object may be able to walk from it to related objects whose resolvers are less carefully guarded. Authorization has to hold at every resolver, including the ones reached past the entry point. The second is that introspection, when left enabled in production, hands an attacker the full schema, including the names of privileged mutations the front end never calls. That does not create a vulnerability by itself, and it removes the guesswork, which is why treating a hidden mutation as protected because the UI never shows it is the same mistake as trusting a hidden REST route.

How to detect API abuse in progress

Prevention is the goal, and detection is what catches the gap you missed. The signals below are concrete enough to build alerts around.

Sequential identifier access. A single session requesting many object identifiers in ascending or descending order, especially across accounts, is the enumeration footprint of a BOLA being exploited. A normal user touches their own handful of objects, not a sweep of the range.

Authorization denials that keep coming. A burst of 403 or 404 responses to one caller across many object identifiers suggests someone probing for the one object the check misses. The denials are the tool working, and the danger is the request that finally succeeds.

Privileged endpoints called by ordinary roles. A request to an admin route from a session that has never held an admin role is a BFLA probe whether or not it succeeds. Log the role on every privileged call and alert when it is wrong.

Response sizes that do not match the feature. Excessive data exposure is visible as responses far larger than the UI uses, or as fields in the wire data that never render. Comparing the fields a client requests against the fields it displays can surface the leak.

Volume out of proportion to the account. One token pulling thousands of records, or hitting a login endpoint far faster than a human could type, points at missing rate limits feeding enumeration or credential stuffing.

How the OWASP API list evolved

The shape of this problem has been stable enough that the OWASP API Security Top 10 has kept authorization at the top across its editions. The first edition put Broken Object Level Authorization at number one and Broken Function Level Authorization high on the list, reflecting that most reported API incidents were authorization failures rather than injection or memory bugs. The later edition kept that emphasis and refined the property-level category, splitting out Broken Object Property Level Authorization to name the two directions of the same trust mistake: reading fields you should not see (the old excessive data exposure) and writing fields you should not set (mass assignment). It also sharpened the resource-consumption entry to cover both cost and rate. The lesson to carry from the history is that the dominant API risks are about who may touch what, and they have stayed that way as the technology underneath shifted from REST to GraphQL to gRPC.

Where API keys and tokens fit

A related class of abuse targets the credential itself rather than the authorization logic behind it. An API key checked into a public repository, embedded in a mobile app binary, or leaked in a client-side bundle gives an attacker a valid caller identity, and from there every authorization gap in the API is theirs to exploit at the privilege level of that key. The defensive habits are separate from the ones above and reinforce them: scope each key to the least it needs, rotate keys on a schedule and immediately on suspected exposure, and never ship a high-privilege key to a client the user controls. A key that can only read one tenant's public data is a far smaller prize than a key that can call every endpoint. When a leaked key meets a BFLA gap, an outsider gains an admin action they were never issued, which is why credential hygiene and authorization discipline have to travel together.

Bearer tokens carry the same lesson. A token that never expires, or one accepted by services it was never issued for, widens the window an attacker has after any single leak. Short lifetimes, audience restrictions that name the intended service, and revocation you have actually tested keep a stolen token from becoming a standing key to the whole surface.

Common misconceptions

A private or undocumented endpoint is safe. Obscurity is not access control. If the route answers, an attacker who finds it, from front-end code, a mobile app, an error message, or a schema, can call it. Treat every reachable endpoint as public.

HTTPS protects the data. Transport encryption stops an eavesdropper on the network. It does nothing about a caller who is authenticated and simply asks for an object they should not receive. Authorization is a separate layer from transport.

Random identifiers fix BOLA. They raise the cost of blind enumeration and authorize nothing. An identifier that leaks is as usable as one that was guessed.

The gateway handles authorization. An API gateway can enforce authentication and coarse policy, and it rarely knows whether this caller owns this specific object. Object-level and field-level authorization belong in the service that has the ownership data.

Frequently asked questions

What is the difference between BOLA and IDOR? They are the same underlying flaw seen from two angles. IDOR is the classic web term for a request reaching an object by a direct reference without an ownership check. BOLA is the API-specific name OWASP uses for it. If you understand one, you understand the other, and the fix is identical: verify on every request that the authenticated user may access the specific object named.

Why is authorization harder to get right than authentication? Authentication is a single question answered once per request: who is this caller. Libraries and identity providers solve it well. Authorization is a question asked at every object and every action, and the answer depends on application data the framework does not know: who owns this record, what role this action requires, which fields this role may set. That per-object, per-field logic lives in your code, so it is where the gaps appear.

Can automated scanners find BOLA and BFLA? Partly. Tools can flag missing authentication and some obvious pattern breaks, and object-level and function-level authorization depend on knowing who should own what, which a scanner cannot infer. The reliable method is testing with two accounts: take an object or action that belongs to user A and attempt it as user B. That check finds the flaw a scanner walks past.

Does rate limiting stop API abuse? It stops the abuse that needs no authorization gap, such as credential stuffing and scraping, and it slows enumeration of a gap that does exist. It does not authorize anything, so a single BOLA still leaks the object it reaches even behind a strict limit. Rate limiting contains blast radius and buys time; the authorization check is what closes the hole.

How does mass assignment relate to these? Mass assignment is the write side of property-level authorization: the API binds request fields onto an object and lets a caller set fields they should not, such as a role or a balance. Excessive data exposure is the read side of the same category. Both come from trusting the shape of the request or the record instead of naming exactly which fields each direction may carry. See Mass Assignment Explained.

What should I test first on a new API? Take one object you own and try to reach it as a different user (BOLA), then try one privileged action as an ordinary user (BFLA), then read a raw response and look for fields the UI never shows (excessive exposure). Those three checks cover the top of the OWASP API list and find most of the serious findings quickly.

API abuse is mostly one bug wearing different clothes: the server knows who is calling and forgets to check what they may touch. Close that gap object by object and function by function, return only the data each caller needs, and rate limit the rest. Those controls turn an API from an open filing cabinet into one that only ever hands each user their own drawer.

Sources & further reading

Sharetwitterlinkedin

Related guides