Skip to content
BuildThenTestedSee packages
CriticalData access

Record ids accepted without an ownership check

Anyone who changes the id in a URL or request can read, edit or delete another customer's record. Rules that decide which user can read or change which rows in the database. Check it yourself with the steps below; our test adds what a script and a second account can find.

What it looks like

// GET /api/invoices/[id]
export async function GET(req, { params }) {
  const invoice = await db.invoices.findUnique({
    where: { id: params.id },
  });
  return Response.json(invoice);
}
An API route (the address a page calls to load data) that fetches a record by the id in the URL and returns it without asking whether the signed-in user owns it. Security testers call this an insecure direct object reference, or IDOR: the id alone is treated as permission.

What happens

Anyone who changes the id in a URL or request can read, edit or delete another customer's record.

Where it shows up

Descriptive, not a verdict: builders and stacks whose defaults make this reachable, or that a public incident names. Each hub states the tool's own security features.

Seen in public

One education app built on Lovable had 16 vulnerabilities, 6 critical; a database function denied callers whose role was 'authenticated', so anonymous callers passed and 18,697 user records were readable without login.

  • 16 Vulnerabilities found
  • 6 Critical
  • 18,697 User records exposed
  • 14,928 Unique email addresses
  • 870 Enterprise users with full PII

Feb 2026 · Taimur Khan · all sources

Check it yourself

Uses your builder's dashboard and your browser. No tools to install.

  1. Create two accounts in your app and add one record, such as an order or a note, in each.
  2. In account A, open a record and copy its address from the browser bar, or open developer tools (right-click, Inspect, then Network) and copy the request that loads it.
  3. Sign in as account B in a private window and paste that address, or use Copy as cURL on the request and run it with account B's cookies.
  4. Try the same with an id one higher or lower than your own, and with the edit and delete actions.

A bad result is account A's record appearing for account B, or an edit or delete going through.

What we check

We enumerate every endpoint that takes an id with two accounts and a signed-out client, including edit, delete and export routes, and try sequential ids as well as ids leaked in other responses. The report lists each route where an id was accepted without an ownership check.

Fix direction

Load records by id and owner together, so the query itself returns nothing for anyone else, or lean on database row rules rather than the route. Random ids help, but the OWASP guidance is clear that they do not replace the ownership check.

Mechanism documented in: Insecure Direct Object Reference Prevention Cheat Sheet (verified 2026-09-21); How to implement authentication in Next.js (verified 2026-09-21).

Covered by

Pre-launch check ($490, 48 hours) and Security sweep ($290, 24 hours)

Both include a re-check after your fixes.

Compare packages

Related patterns

  • Inverted policy logicData access

    Signed-out visitors or other customers can read rows meant for one account, while the owner sees an empty page and nobody notices.

    Critical
  • Authorization enforced only in the browserLogin and permissions

    Anyone who calls the API directly, or edits the page in developer tools, gets the data and actions the screen was hiding.

    Critical
  • Row Level Security disabled on a tableData access

    Anyone with the project URL and the public anon key, both of which sit in the app's JavaScript, can read, edit or delete every row in the table.

    Critical
  • List endpoints that return everythingPerformance

    As the table grows, the response gets slower and heavier until it hits a platform body-size or time limit and the page simply stops loading, usually for your biggest customer first.

    Medium

Questions

what is an idor vulnerability in plain english

IDOR stands for insecure direct object reference. It means the app looks up a record by the id you give it and hands it over without checking that the record is yours. Changing the id in the address bar is enough to see someone else's order, invoice or profile. The two-account test in the self-check shows whether your app has it.

how do i test my app for idor without a developer

Make two accounts, create a record in each, and try to open account A's record while signed in as account B. Do it through the address bar and through the Network tab in developer tools, and try edit and delete as well as view. If anything from A shows up for B, the check is missing. The OWASP IDOR cheat sheet describes the same two-account method.

do uuids prevent idor

Random ids make guessing harder, but ids leak in links, exports and other API responses, so a leaked id still opens the record. OWASP treats random ids as a supporting layer and the per-object ownership check as the control. Ask your developer to confirm every route loads records by id and owner together.

Back to the catalogue, or run the self-check for your stack.