Record ids accepted without an ownership check
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);
}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.
- Create two accounts in your app and add one record, such as an order or a note, in each.
- 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.
- 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.
- 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.
Related patterns
- Inverted policy logicData accessCritical
Signed-out visitors or other customers can read rows meant for one account, while the owner sees an empty page and nobody notices.
- Authorization enforced only in the browserLogin and permissionsCritical
Anyone who calls the API directly, or edits the page in developer tools, gets the data and actions the screen was hiding.
- Row Level Security disabled on a tableData accessCritical
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.
- List endpoints that return everythingPerformanceMedium
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.
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.