You sign in and open /api/orders/41, your order. You change the number to 42 and see someone
else’s. That is an insecure direct object reference (IDOR), also called broken object-level
authorisation, and it is at the top of the OWASP Top 10 as Broken Access Control.
Why a scanner needs two accounts
A page that returns data is not a bug by itself. It is a bug only when the wrong person can read it, and that can only be seen by comparing two identities. SecScan requests the same records as account A, as account B, and as a visitor with no session, and reports a finding when one account can read what belongs to another.
Use two ordinary accounts that own different data. An administrator and a user are supposed to see different things, so they prove nothing.
If you only have one, tick Create one for me on a site you own: the scan then submits your public signup form once, with a made-up example.com address, and uses the new account only if signup gives it a session straight away. It is off unless you choose it, and the report names the account it made so you can delete it.
Two cases are deliberately not reported, because they look like a leak and are not: an app that renders the same shell for every identifier, including one that cannot exist, and a public page that returns the same record to everyone. A real leak in one of those shapes can be missed, which is why the fix below matters more than the scan.
The fix
Check ownership on the server for every request, not in the interface:
- Look the record up by its ID and the signed-in user’s ID (
WHERE id = $1 AND user_id = $2), and return 404 when nothing matches. - With Supabase, do it in a Row Level Security policy so the database enforces it.
- Prefer unguessable IDs (UUIDs) as defence in depth, but never as the only control.
Related
Supabase Row Level Security · Is my AI-built app secure?
Last reviewed 2026-09-30