What Is an IDOR Vulnerability?
An Insecure Direct Object Reference (IDOR) occurs when an application uses a user-controlled value — a number, ID, or filename — to access data, without checking whether the requesting user is allowed to see it.
IDOR is the #1 category in the OWASP Top 10 (Broken Access Control). Despite being one of the oldest and most documented vulnerability classes, we still find it on production platforms serving hundreds of thousands of users.
Real IDOR Examples We've Found
Case 1: Sequential User IDs — 702,651 Records Exposed
# Authenticated user sees their own profile at:
GET /sources/u702651 → {"email":"attacker@...","phone":"555-..."}
# They try the next ID:
GET /sources/u1 → {"email":"[email protected]","phone":"..."}
GET /sources/u2 → {"email":"[email protected]","phone":"..."}
The backend checked isLoggedIn(). It didn't check isOwner().
Result: Every registered user's email and phone number was accessible by any other user.
Case 2: UUID Invoice IDOR — 27,000 Orders Exposed
Some developers switch to UUIDs thinking randomness = security:
GET /i/550e8400-e29b-41d4-a716-446655440000
→ {
"customer": "Jane Smith",
"address": "123 Main St, London",
"vat_number": "GB123456789",
"items": [...],
"total": "£2,499.00"
}
The UUID was embedded in the invoice PDF sent to customers. Whoever received the PDF had the URL. And the URL required no authentication.
We found this on a SaaS platform with over 27,000 orders. Every invoice — name, address, VAT number, order history — was readable without logging in.
Case 3: Ticket Validation Logs — 3,300 Attendee Records
GET /api/validation-logs?event_id=42
→ [{
"attendee_name": "John Smith",
"email": "[email protected]",
"ip_address": "185.234.x.x",
"checked_in_at": "2026-09-15T09:23:11Z"
}, ...]
An events platform stored ticket validation logs — who scanned in at the door — in an API endpoint with no authentication. 3,300 attendee names, emails, and home IP addresses across 20 conferences spanning 8 years.
How to Find IDOR on Your Own Site
Manual Testing (5 minutes)
- Create two test accounts (Account A and Account B)
- With Account A, create a resource (order, profile, document)
- Note the URL or ID used to access it
- Log in as Account B
- Try to access Account A's resource using its ID
If Account B can see Account A's data, you have an IDOR.
What to Check
| Resource Type | What to Test |
| User profiles | /users/{id}, /account/{id} |
| Orders/invoices | /orders/{id}, /invoice/{id} |
| Documents | /files/{id}, /reports/{id} |
| API exports | /export?user_id={id} |
| Admin views | /admin/users/{id} |
The Fix
IDOR is fixed at the data layer, not the route layer:
// Vulnerable — checks auth but not ownership
app.get('/orders/:id', requireAuth, async (req, res) => {
const order = await db.orders.findById(req.params.id)
return res.json(order)
})
// Fixed — checks auth AND ownership
app.get('/orders/:id', requireAuth, async (req, res) => {
const order = await db.orders.findOne({
id: req.params.id,
user_id: req.user.id // must match the authenticated user
})
if (!order) return res.status(404).json({ error: 'Not found' })
return res.json(order)
})
Every data query that takes a user-supplied ID must also filter by the authenticated user's ID.
Find IDOR vulnerabilities on your site →