Broken Object Level Authorization
Change one number in the request and read every other customer's data. That is BOLA.
What you will learn
- -Find BOLA by tampering with object identifiers in paths, bodies, and headers
- -Automate id enumeration to pull a full dataset
- -Test the sneaky variants that live in UUIDs and secondary keys
You are logged in as customer 4021. The app calls GET /api/v1/customers/4021/cards and shows your saved cards. Change the path to 4020 and resend. If another customer's card data comes back, you found Broken Object Level , and you can now walk the entire customer table one integer at a time. This is the same bug as on the web, and on APIs it is number one on the OWASP list because APIs expose object everywhere and re-check ownership almost nowhere.
The did check something. It confirmed your is valid, that you are a logged-in user. What it never did was confirm that customer 4020 is you. passed, authorization was skipped, and that gap is the whole bug.
Find the object references
Object ids hide in more places than the . Before you tamper, map every identifier the app hands you, because any one of them is a candidate.
The obvious one is the path: /orders/8890, /users/1042, /documents/551. The next is the body, where an account_id or owner field decides whose record you touch. Then the sneaky ones: an X-User-Id , a customer query , an id buried in a blob.
$curl .../api/v1/customers/4020/cards -H "Authorization: Bearer $TOKEN"$curl -X POST .../api/transfer -d '{"from":"4020","amount":1}'$curl .../api/profile -H "X-User-Id: 4020"Keep your own token attached the whole time. That is the point: a valid session for you, pointed at someone else's object. You are proving the server authorizes by -of-caller when it should authorize by ownership-of-resource.
Automate the walk
One swap proves the bug. To show , you enumerate. Sequential integer ids make this a loop, and a loop that pulls thousands of records is what turns a finding into a critical.
$for id in $(seq 4000 4100); do curl -s -o "out_$id.json" -w "%{http_code} $id
" "https://target.tld/api/v1/customers/$id/cards" -H "Authorization: Bearer $TOKEN"; doneBurp Intruder does the same thing with a slider on the id position, and it is easier to the results there. Either way, sort by and response length. A 200 with a fat body where you expected a 403 is a hit.
Enumerating ids across live customer data is exactly the act that gets people charged, even when the door was left wide open. Do this only inside a you have written authorization for: OWASP crAPI, VAmPI, PortSwigger's labs, or your own app. Pull the minimum records needed to prove impact, then stop.
When ids are not sequential
UUIDs like 9f1c... break the simple loop, and developers treat that as a fix. It is not. A UUID is an unguessable number, not an access , and it leaks constantly: in a shared-link feature, an autocomplete response, an email, a different that lists other users' ids. Collect UUIDs from one endpoint, feed them to another, and BOLA is back on the table.
Watch for the reverse too. An endpoint might reject /users/4020 but accept the same object through a secondary key, like /users/by-email?email=bob@x.tld or a GET /export?account=4020. Developers guard the main route and forget the alternates. Test every path that reaches the same object.
You own object 4021 and suspect BOLA. Write the single curl request that proves it, and say which two response fields you compare to confirm the hit.