IDOR — Insecure Direct Object Reference — is the deceptively simple bug behind a huge share of real-world data breaches. The whole attack is: change a number in a URL, get someone else's data. No exploit kit, no special tools — just curiosity and a missing permission check.
What IDOR is
An app exposes a reference to an internal object — usually a database ID — in a URL or request body. IDOR happens when the server returns that object without checking the requester is allowed to see it.
# You're logged in as user A, viewing your own order:
GET /api/orders/1001 → 200 OK (your order)
# You change the ID...
GET /api/orders/1002 → 200 OK (someone else's order) ← IDORIDOR is the most common instance of Broken Access Control — OWASP's #1 risk — and in API terms it's called BOLA (Broken Object Level Authorization), the #1 entry on the OWASP API Security Top 10.
Where IDOR hides
- URLs & path params:
/users/123,/invoices/456. - Query strings:
?account_id=789. - Request bodies: a hidden
userIdfield in a POST. - Filenames:
/download?file=report_1001.pdf.
Why "unguessable IDs" don't fix it
A common myth: "we use UUIDs, so IDOR isn't possible." UUIDs make IDs harder to enumerate, but they're not access control. IDs leak — in emails, referrer headers, shared links, other API responses. The moment an attacker has a valid ID, an app without an authorization check hands over the data. Obscurity is not authorization.
How to prevent IDOR
- Check ownership on every object access. Before returning or mutating a resource, confirm the authenticated user is allowed this specific one.
- Scope queries to the user. Don't fetch by ID then check — fetch by ID and owner in one query:
// Vulnerable: fetch then (forget to) check
const order = await db.order.findUnique({ where: { id: params.id } })
return Response.json(order)
// Safe: scope to the owner
const order = await db.order.findFirst({
where: { id: params.id, userId: session.user.id },
})
if (!order) return new Response(null, { status: 404 })- Centralize authorization. A policy layer the whole app uses beats per-endpoint checks you'll forget.
- Use RLS for direct-to-DB apps. If clients query the database directly (Supabase), Row Level Security enforces ownership at the database.
- Test it. Log in as two users; try to access each other's resources. The second must get 403/404.
Find your IDORs before someone else does
IDOR is invisible to the developer who built it — the app works fine with your data. Nurbak scans your deployed app for broken object-level authorization and the other issues on the API security checklist, so you catch the missing check before an attacker enumerates your IDs.

