← all writeups
API

BOLA, Mass Assignment and the API Bugs That Still Pay in 2026

Oct 11, 2026 · 10 min · by Abhishek Bhaskar (Abhi HackZ)
BOLA, Mass Assignment and the API Bugs That Still Pay in 2026 — cover

The OWASP API Security Top 10 has not changed since the 2023 edition. The apps behind it have. Single-page frontends talk to a back-end-for-frontend, which talks to a GraphQL gateway, which fans out to a dozen microservices. Mobile clients hit the same APIs directly. New LLM features bolt a chat backend onto endpoints never designed to be called that way. The surface moved, the categories did not, and the same authorization bugs that paid in 2020 still pay now, because they live in business logic that no scanner understands.

This is how I test modern APIs for the categories that actually produce accepted reports: object-level authorization (BOLA), authentication, property-level exposure and mass assignment, function-level authorization, plus the GraphQL and resource-consumption angles. Examples use example.com, placeholder tokens and lab data. The goal is a method a junior tester can reproduce, not a list of endpoints to blindly fuzz.

What changed: BFFs, GraphQL, mobile-first APIs, LLM backends

A few shifts matter for testing:

None of this invents new bug classes. It moves the old ones to places people forgot to check.

BOLA (API1): the test matrix

BOLA stands for Broken Object-Level Authorization: the API accepts a request that references an object by its identifier and returns or modifies it without checking that this caller is allowed to touch that object. It is number one on the OWASP API list for a reason: it is the single most common serious API bug, and it is invisible to the UI because the UI only ever shows you your own IDs.

The assumption BOLA violates is "the client can only ask for what we showed it". Attackers do not use your client — they change the ID.

The minimal mechanism, horizontal (same privilege, different tenant):

GET /api/v1/invoices/1001 HTTP/1.1
Host: api.example.com
Authorization: Bearer USER_A_TOKEN
GET /api/v1/invoices/1002 HTTP/1.1
Host: api.example.com
Authorization: Bearer USER_A_TOKEN

If 1002 belongs to user B and user A gets it back, that is BOLA. The fix is a server-side check that the object's owner matches the authenticated principal, on every object access, not just at the collection level.

My test matrix, run for every object-bearing endpoint:

The misconception to kill: people treat UUIDs as an access control. They are obscurity. Authorization is a check you perform, not a hard-to-guess string.

Broken authentication (API2) in token-heavy apps

Modern APIs lean on bearer tokens, usually JWTs (JSON Web Tokens — signed, self-describing tokens the server validates without a session lookup). The bugs cluster around how the token is validated.

What I check on every JWT:

The principle: a token proves who you are (if validated correctly), never what you may touch. Authentication and authorization are different checks; passing the first tells you nothing about the second.

Property-level authorization (API3): over-fetching and mass assignment

API3 is two sides of one coin: the API exposes more than it should on the way out (over-fetching) or accepts more than it should on the way in (mass assignment).

Over-fetching. The UI shows a user's name and avatar. The API response also carries role, isVerified, internalNotes, passwordResetToken — the UI hides them, the API hands them over. Always read the raw response, never the rendered screen.

Mass assignment is the inbound version, and it still pays well. The server binds the whole request body onto the object, including fields the client was never meant to set. The classic:

PATCH /api/v1/users/me HTTP/1.1
Host: api.example.com
Authorization: Bearer USER_TOKEN
Content-Type: application/json

{ "displayName": "Abhi", "role": "admin", "accountId": 42 }

If role or accountId sticks, you have a privilege or tenant escalation. To find the extra fields, diff what the API returns against what the UI uses, then try writing back every field you saw plus obvious guesses (isAdmin, verified, ownerId).

The assumption being violated: "the client only sends the fields in our form". The fix is an explicit allow-list of writable fields per endpoint and role — bind to a DTO you define, never straight onto the database model.

Function-level authorization (API5) and hidden admin routes

BOLA is about objects; BFLA (Broken Function-Level Authorization, API5) is about operations. Can a regular user call an admin function? The method and the route are the test, not the object.

The fix is the same shape as BOLA: enforce the role check server-side on every function, deny by default.

GraphQL specifics

GraphQL concentrates several of the above into one endpoint, so I give it dedicated passes.

Rate limiting and unrestricted resource consumption

API4 (Unrestricted Resource Consumption) is less glamorous but real: endpoints that do expensive work with no ceiling. I check whether a single caller can force heavy queries (deep GraphQL nesting, large page sizes, wide date ranges), whether rate limits are per-user or trivially bypassed by rotating tokens, and whether batching sidesteps limits. On its own it is often low-to-medium; it matters most as an amplifier for the bugs above.

Reporting severity honestly

This is where reports get rejected, and where I see hunters lose credibility. A BOLA that leaks one non-sensitive field is not critical because you called it "full account takeover". Rate the finding on demonstrated impact and the sensitivity of what was actually exposed.

Where severity is earned is in chains. A BOLA that leaks an email, plus a mass-assignment that sets a role, plus a function-level gap on an admin route, can be a genuine account-takeover chain — show it step by step, with requests and responses, rather than inflating any single link. Honest severity gets you taken seriously next time, which is worth more than one padded report.

Checklist

Fixes

Takeaways

The API Top 10 did not change because the hard problems did not: authorization is business logic, and business logic is exactly what automated tools cannot reason about. BOLA, mass assignment and broken function-level authorization keep paying because every new architecture — BFFs, GraphQL, mobile, LLM backends — just relocates the missing check. UUIDs are not authorization. Authentication is not authorization. The UI is not authorization. Test with two accounts and the raw wire, read every field the server returns, try to write every field it accepts, and report what you actually proved.

Learn more

Related: Framework-Level Bugs: Middleware Bypasses, SSR Fetches and the SSRF Hiding in Your Renderer · Testing AI Agents and MCP Servers. Need this tested on your product? Book a free 30-minute scoping call.

KEEP READING
Abhishek Bhaskar (Abhi HackZ)
Security Analyst & VAPT Engineer
Book a free 30-min security scoping call →