Broken Object Level Authorization is the number one issue on the OWASP API Security Top 10, and it's also the bug most people test for wrong. They send one request, see their own data come back correctly, and move on. BOLA doesn't show up when you request your own object. It shows up when you request someone else's.
Here's the methodology that actually surfaces it.
Map every endpoint that takes an identifier
Before touching anything, walk the API and list every route that includes an object ID: /api/orders/1042, /api/users/318/invoices, /api/messages/9981. Anything with a numeric ID, a UUID, or a slug in the path or body is a candidate. Don't skip the write endpoints (PUT, PATCH, DELETE) to focus only on GET — a BOLA on a PATCH that lets you edit someone else's order is a worse finding than one that just lets you read it.
Get two accounts, not one
You cannot test authorization with a single account, because a single account can only prove authentication works. Register two low-privilege users, A and B. Log in as A, capture the ID of an object A owns, then replay that exact request with B's session token or cookie instead. If B gets A's data back, that's BOLA. If B can modify or delete it, that's worse.
UUIDs are not a fix
A lot of teams switch sequential integer IDs to UUIDs and consider the object-reference problem solved. It isn't. A UUID is unguessable, but that only matters if the ID is never exposed anywhere else. In practice IDs leak constantly: in another API response, in an invite link, in a webhook payload, in a support ticket URL. If the authorization check on the endpoint is still "does an object with this ID exist" instead of "does an object with this ID belong to the requesting user," the UUID just raises the cost of finding the ID, it doesn't fix the missing check.
Test both directions
Horizontal BOLA is user A reaching user B's data at the same privilege level. Vertical is a regular user reaching an admin-only object or action. Test both, and test them at every parameter that references an object, including ones buried in the body or in query strings rather than the path, since those get missed by anyone only diffing the URL.
Watch for indirect BOLA through relationships
The subtler version: endpoint A properly checks ownership, but endpoint B, which returns a nested object (a comment on an order, an attachment on a ticket), doesn't re-check ownership of the parent. You authenticated for the comment, not for the order it belongs to, and the API assumed that was enough. This is the class of bug that survives a first authorization pass because every individual endpoint looks correct in isolation.
Read the response, not just the status code
A 403 or 401 is the easy signal. The bug that gets missed is the 200 that returns a slightly different, sanitized version of someone else's object, a partial BOLA where fields you shouldn't see (an email, an internal note) are present but the object itself claims to be "not found" everywhere else. Diff full response bodies between accounts, not just status codes.
This is the exact class of bug the OWASP API Top 10 puts first for a reason: it doesn't require finding an exploit chain, it requires noticing that a check that should exist doesn't. Codelivly's API Hacking Book for Beginners walks through this methodology end to end against real API attack surfaces, alongside JWT and SSRF abuse. If you want to drill it hands-on first, the IDOR lab and the HelpHub IDOR CTF challenge are free and built around exactly this two-account testing approach.
Top comments (0)