API Security Broken Object Level Authorization BOLA IDOR
Broken Object Level Authorization — known as BOLA, or by its older name IDOR (Insecure Direct Object Reference) — is consistently ranked as the number one vulnerability in the OWASP API Security Top 10. It is simple to exploit, common in production systems, and can expose the data of every user in a system with minimal technical skill.
What Is BOLA
BOLA happens when an API uses a predictable identifier — like a number or username — to access a resource, but does not verify whether the requesting user actually owns or is authorized to access that resource.
Simple Analogy: Imagine a bank where every safe deposit box has a number. You are assigned box number 501. The bank employee gives you a key and says: "Your box is number 501." You notice the boxes go 499, 500, 501, 502, 503... You try inserting your key into box 502. The lock opens. You access someone else's box. The bank only checked that you had a valid key (authentication). It did not check that the key was FOR box 502 (authorization). This is BOLA.
BOLA in API Requests
Example: A document management API
Legitimate user Priya (user_id: 201) requests her document:
GET /api/documents/7701
Authorization: Bearer priya_valid_token
Server response:
{ "id": 7701, "title": "My Tax Return 2024", "owner": 201, ... }
Priya modifies the document ID:
GET /api/documents/7702
Authorization: Bearer priya_valid_token
Server response (if BOLA exists):
{ "id": 7702, "title": "Arjun's Salary Slip", "owner": 202, ... }
Priya's token is valid (authentication passed).
But she is not authorized to view document 7702 (authorization failed).
The server returned document 7702 anyway.
Priya can repeat this for 7703, 7704, 7705...
Scraping every document in the system by counting upward.
Why BOLA Is So Common
BOLA occurs because developers implement authentication checks — "Is this user logged in?" — but forget to implement object-level authorization checks — "Does this user own this specific resource?"
Vulnerable code example (Node.js):
router.get('/api/documents/:documentId', authenticateToken, async (req, res) => {
const document = await db.findDocument(req.params.documentId);
// ← MISSING: Check that document.owner === req.user.id
if (!document) return res.status(404).json({ error: 'Not found' });
res.json(document); // Returns any document to any authenticated user!
});
The authenticateToken middleware correctly verifies the JWT.
But nothing checks whether the user owns documentId.
Fixed code:
router.get('/api/documents/:documentId', authenticateToken, async (req, res) => {
const document = await db.findDocument(req.params.documentId);
if (!document) return res.status(404).json({ error: 'Not found' });
// ADDED: Ownership check using server-side user identity
if (document.ownerId !== req.user.id) {
return res.status(403).json({ error: 'Access denied' });
}
res.json(document);
});
BOLA Attack Patterns
Pattern 1: Sequential ID Enumeration
APIs using sequential integer IDs are highly vulnerable:
/api/orders/1001
/api/orders/1002
/api/orders/1003
...
/api/orders/9999
Attacker writes a simple script:
for id in range(1, 10000):
GET /api/orders/{id}
if status == 200:
save(response)
In minutes, the attacker has every order in the system.
Customer names, addresses, purchase history, payment info.
Pattern 2: GUID/UUID Enumeration (Less Obvious)
Some developers think using UUIDs instead of sequential IDs prevents BOLA. This is not true — it only makes the IDs harder to guess, not impossible to find. If the API returns UUIDs in other contexts (shared links, email confirmations, other API responses), attackers collect them from these sources. Example: Order confirmation email includes: "Track order: /api/orders/550e8400-e29b" Attacker who receives any email can access any order whose UUID they have. The fix remains the same: verify ownership, regardless of ID format.
Pattern 3: Parameter Tampering in Request Body
BOLA does not only appear in URL paths. It appears in request bodies too.
Updating a profile:
PATCH /api/users/profile
Body: { "user_id": 201, "email": "new@example.com" }
Tampered request:
PATCH /api/users/profile
Body: { "user_id": 202, "email": "hijacked@attacker.com" }
If the server uses the user_id from the request body (instead of from the
verified JWT token), the attacker updates user 202's email.
→ Account takeover via BOLA.
Fix: Always get the user ID from the verified token — never from the request body.
Pattern 4: Function-Level BOLA in Relationships
APIs with relationships between objects create multiple BOLA opportunities. Example: Project management API User belongs to Company Company has Projects Projects have Tasks Correct authorization chain: GET /api/companies/10/projects/50/tasks/300 Server must verify: 1. Company 10 belongs to the requesting user's organization ✓ 2. Project 50 belongs to Company 10 ✓ 3. Task 300 belongs to Project 50 ✓ 4. User has permission to view tasks in this project ✓ Missing ANY link in this chain creates BOLA. An attacker who is a member of Company 10 might access: /api/companies/99/projects/50/tasks/300 → If company check is missing, accesses Company 99's data.
Real-World BOLA Examples
Parler (2021): Social media platform used sequential post IDs. No authentication required for viewing posts. Scraper downloaded every public post by counting from 1 to millions. Location metadata embedded in posts exposed user locations. Instagram (2019): Bug bounty researcher found BOLA in private story viewer endpoint. Could access private stories by changing the media ID in requests. Instagram fixed it and awarded a bug bounty. US Postal Service (2018): API for mail tracking used account IDs that could be modified. Any logged-in user could query other users' account details. Affected over 60 million USPS customer records. Venmo (2019): Public API returned all transactions without authentication or rate limits. Researchers downloaded over 200 million public transactions. Exposed spending patterns, political donations, personal relationships.
Prevention Strategy
Rule 1: Always Verify Object Ownership
Every endpoint that accesses a specific resource must verify: "Does the authenticated user have permission to access THIS resource?" This check must use the user ID from the verified token: ✓ req.user.id (from JWT payload, server-verified) ✗ req.body.user_id (from request body, attacker-controlled) ✗ req.query.user_id (from URL parameter, attacker-controlled) ✗ req.headers['x-user-id'] (from header, attacker-controlled) Never trust any user identity from the request itself. Only trust identity from the cryptographically verified token.
Rule 2: Build Authorization Into Database Queries
Instead of: SELECT * FROM documents WHERE id = ? (then check owner in application code) Use: SELECT * FROM documents WHERE id = ? AND owner_id = ? (bind document_id AND authenticated_user_id) If the document exists but belongs to someone else: The query returns no rows. Application returns 404 or 403. Attacker cannot confirm whether the document exists. Benefit: Even a programming error in the application layer cannot return unauthorized data — the DB query itself enforces it.
Rule 3: Use Non-Guessable IDs
Sequential IDs (1, 2, 3...) make enumeration trivial. UUIDs make guessing nearly impossible. UUID example: /api/documents/550e8400-e29b-41d4-a716-446655440000 Even if someone finds your API, they cannot guess valid UUIDs. This is defense in depth — not a replacement for authorization checks. Generate UUIDs server-side, never client-supplied. Use UUID v4 (random) not UUID v1 (time-based, partially predictable).
Rule 4: Rate Limit and Monitor for Enumeration
Detection pattern for BOLA attacks:
- Single user accessing many different resource IDs in rapid succession
- Sequential ID pattern in requests (100, 101, 102, 103...)
- High 200/403 ratio suggesting hit-and-miss enumeration
- Same source IP accessing resources across many different users
Alert rules:
User accesses more than 50 different document IDs in 10 minutes → Alert
Requests to /api/orders/{id} with sequential IDs → Block + Alert
IP accessing resources belonging to more than 5 different users → Flag
Rule 5: Test Every Endpoint Systematically
BOLA testing methodology: Step 1: Create two test user accounts (User A and User B) Step 2: With User A, create resources (orders, documents, profiles) Step 3: Note the IDs of User A's resources Step 4: Log in as User B Step 5: Attempt to access User A's resources using their IDs Step 6: Any successful access (200 OK) is a BOLA vulnerability This test must be run on EVERY endpoint that takes a resource ID. Automated tools like OWASP ZAP, Burp Suite, and purpose-built BOLA scanners can help systematize this testing.
Key Points
- BOLA (formerly IDOR) is the most common API vulnerability. It happens when authentication is checked but object ownership authorization is not.
- Attackers exploit BOLA by modifying resource IDs in URLs, request bodies, or query parameters to access other users' data.
- Sequential integer IDs make BOLA trivially easy to exploit through automated enumeration. UUIDs slow down attackers but do not replace authorization checks.
- Always derive user identity from the verified JWT or session token — never from any user-supplied value.
- Build ownership checks directly into database queries using WHERE owner_id = ? conditions for maximum safety.
- Monitor for sequential ID access patterns and high request rates to single resource types as indicators of BOLA attacks.
