IDOR — Insecure Direct Object Reference — is the best vulnerability class for a new bug bounty hunter to master. It requires no exotic payloads, no memorizing encodings, just careful observation and patience. It’s also everywhere, and programs pay well for it because it exposes real user data. This is a practical, beginner-friendly methodology for finding your first IDOR — ethically and safely.
What an IDOR actually is
An IDOR happens when an application uses a user-supplied identifier to fetch an object — a record, a file, an order — without checking whether you’re allowed to access that object. The classic example: you view your own invoice at
GET /api/invoices/1042
…and out of curiosity you change 1042 to 1041. If the server hands you someone else’s invoice, that’s an IDOR. The app authenticated you but never authorized the object. That gap is the entire bug.
The golden rule: use two accounts, never real users’ data
This is non-negotiable and it’s also how you prove the bug cleanly. Create two test accounts — call them Account A and Account B. Everything you do, you do between accounts you control. When you find an object belonging to Account A, you try to access it as Account B. If it works, you have proof with zero harm done.
Never pull data belonging to real users. It’s unethical, it can be illegal, and it will get you banned from the program. If an IDOR looks real, demonstrate it with your own two accounts and describe the impact — the triager doesn’t need you to breach a stranger’s account to understand it.
Step 1 — Map every object reference
Browse the application as Account A with your proxy (Burp or Caddy) recording everything. Then hunt the traffic for identifiers you supply:
- Numeric IDs in paths:
/users/500,/orders/1042,/documents/88 - IDs in query strings:
?id=500,?account=1042,?file=report.pdf - IDs in request bodies:
{"userId": 500},{"invoice_id": 1042} - IDs in headers or cookies
Write down every endpoint that takes an object identifier. Each one is a candidate.
Step 2 — Understand the ID format
The ID format tells you how easy the bug is to exploit:
- Sequential integers (
1041,1042) — trivial to guess. If access control is missing, an attacker can enumerate every record. - UUIDs (
3f2a…) — hard to guess, but still an IDOR if the app leaks them somewhere (in a list endpoint, an email, a shared link, a referrer). Unguessable is not the same as authorized. - Encoded/hashed IDs — decode them first. A base64
MTA0Mg==is just1042wearing a costume.
Step 3 — Swap and observe
Now the core test. Take a request that fetched Account A’s object, and replay it as Account B — same object ID, but Account B’s session token/cookie. The cleanest way is Burp’s “match and replace” or simply swapping the Authorization header between two logged-in sessions.
Three outcomes:
- 403 / 401 / “not authorized” — access control works. Move on.
- 200 with Account A’s data — IDOR confirmed. Account B just read Account A’s object.
- 404 or empty — ambiguous; the app may be hiding existence. Note it and probe further.
Step 4 — Don’t stop at reads
Reading another user’s data is a finding. Modifying it is a bigger one. For every object you can read cross-account, test whether you can also write it — swap the ID on PUT, PATCH, POST, and DELETE requests too. An IDOR that lets Account B edit or delete Account A’s records is a far more severe, better-paying bug than a read-only leak.
Common places IDORs hide
- API endpoints the mobile app uses (often less hardened than the web app)
- “Export”, “download”, “invoice”, and “report” features that take a file or record ID
- Anything with
/admin/,/manage/, or a tenant/organization ID in the path - Second-order references — you can’t set the ID directly, but a related field (
referrerId,parentId) flows into a lookup - GraphQL
node(id:)queries and any resolver that takes an ID argument
Writing it up
A great IDOR report is short and unambiguous: the endpoint, the two accounts, the exact swapped request, and the response proving cross-account access. State the impact in one line — “any authenticated user can read/modify any other user’s X” — and note the scale (sequential IDs = full enumeration). That clarity is what gets a report triaged fast.
The bottom line
IDOR rewards patience over cleverness. Enumerate every object reference, understand the ID format, and swap between two accounts you control — reads first, then writes. No fancy payloads, just the discipline to test authorization on every object the app touches. It’s the most beginner-accessible path to a real, paying finding.
Test only within an authorized bug bounty scope, only with accounts you own, and never access real users’ data. Responsible testing is what keeps bug bounty legal and welcome.