An insecure direct object reference is a bug where an app looks up a record by its ID and never checks whether you're allowed to have it. People shorten that to IDOR. The "object" is the record, the "direct reference" is the ID pointing straight at it, and the "insecure" part is the missing check.
Sites use IDs all the time. A number in an address bar is normal, and it isn't a bug. Changing it on a real site is testing that site, though. Get permission before you access anything, stay in the practice labs, and never touch a real site. Being able to see something doesn't mean you're allowed to see it.
A made-up pet clinic
All the people, pets and records here are made up. A pet clinic has an app where owners read their pet's visit notes. Mia signs in and opens her dog Biscuit's record at /records/2041. Theo's cat Pickles is record 2042.
The ID tells the server which record you want, and that's all it does. It's an address, and an address doesn't say whether you're allowed inside.
What the missing check looks like
Here's what a server with this bug does when a request for record 2042 comes in:
- It checks that someone is signed in. Mia is.
- It finds record 2042.
- It sends it back.
So Mia sees Pickles' visit notes. There's no error and no warning, and the page looks normal. People usually open only their own records, so everything works until someone asks for one that isn't theirs.
Signing in answers "who are you?" It doesn't answer "is this yours?" Checking the second one is called authorization, and that's the step this server skipped.
The fix is one question
Before the server sends anything back, it asks whether this record belongs to the person asking. Record 2042 belongs to Theo and Mia is asking, so the server refuses.
That check goes on the server, and it goes on every action: view, edit, delete, download. If the server checks ownership when you read a record and skips it when you delete one, the bug is still there.
Sometimes the question is wider than ownership. A vet at the clinic should be able to open Pickles' record too. Either way, the server decides from who's signed in, every time.
Long random IDs don't fix this. They're harder to guess, but anyone who gets hold of one still gets the record, because the check is still missing. They're a fine extra layer on top of the check.
One record is enough
In a practice app, the proof is small. You use your own practice accounts, and you write down who you were signed in as, what you changed, what came back and what should have happened. Someone reading that can repeat it and see the missing check.
Then you stop. A second record proves nothing the first one didn't. If the server hands over one record it never checked, you can already explain the impact without counting what else is there. More records make the report worse, because every one of them is something that wasn't yours to take.
BugClub HQ's ethics agreement says: "Once you've shown a bug is real, stop. Don't dig further, don't collect anyone's data, don't show it off."
If you notice something like this on a real site, don't try another number to check. Seeing it doesn't make it yours to test.
Week 6 of the curriculum covers this bug on a practice notebook app, and ends with a write-up of the steps, the impact and the fix.