Changing one number opened someone else's note

In this controlled demo, Alice opens her note at /notes/1, changes it to /notes/2 and reads Bob's note. She is logged in, so the server assumes she is allowed.

Technical name: Missing ownership check (IDOR)Controlled demo · fake data

Can users open other users' records by changing the ID in the URL?

Yes, if the server loads records by ID without checking who owns them. Being logged in proves who someone is, not what they own. The fix is to look records up by ID and owner together on the server, and return 403 or 404 for anything that isn't theirs.

How to tell if your app has this

No code needed.

  1. Create two test accounts, A and B, and give each one a record (a note, order or invoice).
  2. As A, open your own record and look at the address bar. Change the ID to B's record.
  3. If B's record opens, the ownership check is missing. Try the same for edit and delete actions.

Before and after

Before
demo-notes.app/notes/2
Signed in as Alice
Bob's note
Private demo note. Owner: bob

Alice, logged in, reading Bob's note.

After
demo-notes.app/notes/2
Signed in as Alice
403 FORBIDDEN
Not your note.

Same request for Bob's note: 403. Her own note still opens.

Why AI-built apps end up with this

A request like "show the note with this ID" is the simplest thing that works, so that is often what gets generated. Ownership is a rule about your business, not about the feature, and it only shows up as a problem when a second user exists.

Fix it yourself

How do I fix it? Paste this into your AI tool

In my app any logged-in user can open any record by changing the ID in the URL (for example /notes/2). Change every read, update and delete endpoint so the server only returns records owned by the signed-in user (filter by owner = current user on the server) and returns 403 or 404 otherwise. Random IDs are not a fix on their own.
Works with Lovable, Bolt, Cursor, Replit and similar tools. Then run the check-up below on your own app.

How we checked the fix

Same request before and after the fix
Same requestBeforeAfter
Alice asks for Bob's noteBob's note403
Alice asks for her own noteNot testedAllowed

Results from our demo test, run against the same demo before and after the fix.

Common mistakes when fixing this

  • Being logged in proves who someone is, not what they own.

Questions people ask

Will random or UUID IDs fix it?

No. Random IDs make guessing harder but IDs still leak through shared links, logs and screenshots. The server must still check ownership on every request.

Should I return 403 or 404?

Either works. 404 hides that the record exists at all, which some apps prefer. What matters is that the record's data never comes back.

Does this affect edits and deletes too?

Yes. The same missing check often lets users change or delete other people's records. Test read, update and delete separately.

For developers: the cause and the fix in code
− note = notes[note_id]
+ note = notes[note_id]
+ if note.owner != session.user:
+     return 403

Simplified. Your stack will look different; the principle is the same.

Learn more