I was poking around an API that my Friend built — just a simple shop backend. No fancy user interface, just raw requests. Everything looked fine at first. But then I noticed something… unfinished.
So I started testing, request by request.
And then I saw it.
Not an error. Not a crash.
😱 Something way worse.
Let me walk you through what I discovered, how I found it, and why this is one of the most dangerous API flaws in modern applications.
🚀 The Setup: API Testing with Postman
I started with the basics. I fired up Postman, added my API key and user number:
GET /api/user/2?apiKey=abc123
Boom, I got a response. The data matched my account. Everything looked normal…
…Except one thing: the response included the password in plain text.
I brushed it off — maybe it’s just test data.
But something else caught my eye.
This number in the URL:
/api/user/2
Could this number be manipulated?
So I changed the 2 to a 3.
Sent the request again:
GET /api/user/3?apiKey=abc123
💥 Boom. I got someone else’s data.
Same format. Different user. No errors. No login bypass.
I just changed a number.
That’s when it hit me.
🧠 Broken Object Level Authorization (BOLA)
I was staring at a classic vulnerability:
Broken Object Level Authorization (BOLA)
Here’s what it means:
- The server checks if the API key is valid ✅
- But it never checks if that key is allowed to access the requested user’s data ❌
So with a valid key for user 2, I was able to access user 3, user 4… anyone.
🔄 Enumerating Other Users
I wanted to know: how many users could I enumerate?
So I used Burp Suite with a payload brute-force technique:
Burp Setup
- Copied HTTP request from Postman
- Pasted into Repeater
- Sent it to Intruder
- Chose Sniper attack type
- Set payload position to the user ID
- Configured numbers from
0to100
Sample Request Template
GET /api/user/§ID§?apiKey=abc123
I started the attack.
- Some came back
500 Internal Server Error - Others came back
200 OKwith full user data
📜 Names, emails, even passwords — all in plain text.
🧨 Why This Is Dangerous
Most APIs use predictable IDs like 1, 2, 3…
With no authorization check tied to the object, you can enumerate users.
It’s one of the top issues in the OWASP API Security Top 10.
This isn’t theoretical. This is real and dangerous.
🛡️ How to Fix It: Never Trust the Client
If the client says:
GET /api/user/3?apiKey=abc123
The server should never trust that the user is allowed to access user 3.
But in many APIs, that check is missing.
❌ Vulnerable Code (Broken BOLA)
@app.route("/api/user/<id>")
def get_user(id):
api_key = request.args.get("apiKey")
user = find_user_by_api_key(api_key)
return get_user_by_id(id)
✅ Secure Code (BOLA Fixed)
@app.route("/api/user")
def get_authenticated_user():
api_key = request.args.get("apiKey")
user = find_user_by_api_key(api_key)
if not user:
return {"error": "Unauthorized"}, 401
return user.to_dict()
🔐 Why This Works
- No user ID is sent in the URL
- The server derives the user from the API key
- Even if the client tries to access someone else’s data, it won’t work
💡 Lessons Learned
- Never trust input from the client
- Always enforce access control on the server side
- Avoid exposing predictable IDs if possible
🎯 Real-World Risk
That was just one API.
Imagine hundreds — in fintech, healthcare, education — all making the same mistake.
Unprotected. Exposed. Vulnerable.
Stay curious, stay safe. 🛡️