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)  ← IDOR

IDOR 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 userId field 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

  1. Check ownership on every object access. Before returning or mutating a resource, confirm the authenticated user is allowed this specific one.
  2. 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.

Related articles