Skip to content

Change one number in the URL. If you see someone else's data, you have the most common serious flaw on the web.

An insecure direct object reference (IDOR) is when an app shows or changes a record just because someone asked for its ID, without checking that the record belongs to them. It's the simplest form of broken access control, which is ranked first in the OWASP Top 10:2025. In 2018 a flaw of this kind in a USPS API let any logged-in user look up account details of around 60 million other users. To test your app, create two accounts, note an ID belonging to the first, and request it while signed in as the second. If it loads, the fix is a server-side ownership check on every request.

By VibeGuard Team4 min read

Here's a test you can do right now. Sign in to your app and open any page that shows a single record: an invoice, an order, a project, a profile. Look at the URL. There's a good chance it contains an ID, something like /invoices/1042 or /projects/8f3a2c.

Now change it to another one. Try 1041.

If you're looking at someone else's invoice, you've found an insecure direct object reference, usually shortened to IDOR. It's the simplest version of the most common serious flaw in web applications, and apps built with AI tools are especially prone to it.

Why it's number one

OWASP, the non-profit behind the best-known list of web application risks, ranks broken access control first in its Top 10:2025. It held the same spot in the 2021 edition. Its API security list has a separate entry for this exact pattern, called broken object level authorization.

It's common because it's invisible. The page works. The feature works. Nothing errors. The app returns invoice 1041 perfectly to whoever asks. It just never checked whether the person asking should see it.

A well-known example: in 2018, security journalist Brian Krebs reported that an API behind the US Postal Service website let any logged-in user query account details for other users, including email addresses, usernames, street addresses and phone numbers. Around 60 million users were affected. The researcher who found it had reported it a year earlier. You needed an account, but any account would do.

Why AI tools produce it so often

When you ask an AI tool for "a page that shows an invoice", it builds exactly that: take the ID from the URL, fetch the invoice, render it. Checking that the invoice belongs to the signed-in user is a separate requirement, and nothing in the request mentioned it.

The model isn't being careless. It's doing what it was asked. Security requirements that depend on your business ("customers only see their own invoices, accountants see all invoices for their client") need to be stated, and founders rarely state them because they seem obvious.

The ten-minute test

You need two accounts. Call them A and B.

  1. Sign in as A. Create something private: an invoice, a document, a note, an order. Anything that belongs to A.
  2. Find its ID. It's often in the URL. If not, open the browser's developer tools (F12), go to the Network tab, reload, and look at the requests your app makes. The ID will be in one of them.
  3. Sign out and sign in as B.
  4. Paste A's URL into the address bar. Does A's record appear?
  5. Now try the underlying request. In the Network tab, find the request that loaded one of B's own records, right-click it, and choose "Copy as fetch" or "Copy as cURL". Ask your AI tool to swap B's record ID for A's and run it. Does A's data come back?
  6. Try the same for editing and deleting, if your app has those actions. These are often less protected than viewing.

If any step shows or changes A's data while you're signed in as B, you have an IDOR.

Also try step 5 with no one signed in at all. Some endpoints skip the login check entirely.

The fix

The fix is always the same principle: every request that touches a specific record must check, on the server, that the signed-in user is allowed to touch it.

In a traditional backend, that's a line in each route: fetch the invoice where the ID matches and the owner is the current user. In a Supabase app, it's a Row Level Security policy that ties rows to auth.uid(), which our plain-English RLS guide walks through.

You can hand this to your AI tool as a prompt:

For every API route and database query that reads, updates or deletes a single record by ID, add a server-side check that the signed-in user owns that record or has an explicit role that allows access. Return 404 if not. List every route you changed.

Then run the two-account test again. Don't take the tool's word that it's done.

Things people think fix it, but don't

Hiding the ID from the URL. The ID is still in the network requests, where anyone can see it.

Random IDs instead of numbers. They make guessing harder, which is worth having, but IDs leak through shared links, emails and API responses. A check on the server is still needed.

Checking in the frontend. If the page hides A's invoice from B but the API still returns it, the API is what an attacker talks to.

This test is item two in our pre-launch security checklist, right after checking your database rules. Between them they cover most of the serious findings in AI-built apps.

Questions founders ask

Do random IDs (UUIDs) fix this?

They make IDs hard to guess, which helps, but they don't fix it. IDs leak through shared links, emails, browser history, logs and API responses that list other records. The only fix is checking on the server that the signed-in user is allowed to see that specific record.

Does this apply to apps using Supabase?

Yes. In a Supabase app, the ownership check is usually a Row Level Security policy. If the policy only checks that someone is logged in, any user can fetch any row by its ID. The two-account test catches this.

What does 'broken access control' mean?

It's the general category for any case where a user can do or see something they shouldn't: other people's data, admin functions, paid features. Changing an ID is the most common and easiest-to-test example.

Sources

  1. OWASP Top 10:2025 · OWASP Foundation
  2. API1:2023 Broken Object Level Authorization · OWASP API Security Project
  3. USPS Site Exposed Data on 60 Million Users · Krebs on Security, 21 Nov 2018

Is your app one of them?

An independent security check of your live app, from €59, with fix prompts for the coding tool you already use. Read-only, and it never touches your code.

Check my app

Keep reading

All articles