It was 2:00 AM. I had been poking at a fintech app for six hours, finding nothing. Then I changed one number in a URL. The response came back with someone else's full bank statement โ name, account number, transaction history. One digit. A complete stranger's financial life.
That's the terrifying simplicity of IDOR โ Insecure Direct Object Reference. It's consistently one of the most rewarded vulnerability classes in bug bounty programs, yet developers keep introducing it and hunters keep cashing in. Let's break down exactly why, and how you can find it yourself.
What is IDOR, exactly?
IDOR occurs when an application exposes a reference to an internal object โ a database row, a file, a user account โ and trusts that reference without verifying whether the requesting user actually has permission to access it.
Simple definition: The app lets you supply an ID for something. It fetches that thing. It never checks if it's yours.
The classic form looks like this:
# You're logged in as user 1042. You notice this request:
GET /api/invoices/8831
Authorization: Bearer eyJhbGc...
# So you try:
GET /api/invoices/8830
Authorization: Bearer eyJhbGc...
# Response: 200 OK - another user's invoice. Bug confirmed.The server authenticated you just fine. It validated your JWT token. But it never asked: "Is invoice 8830 owned by the user who owns this token?" That missing check is the entire vulnerability.
Why does this keep happening?
You'd think after years of OWASP Top 10 awareness (IDOR lives inside Broken Access Control, the #1 category since 2021), developers would be on guard. They are โ sometimes. The problem is scale and inconsistency.
A modern API might have hundreds of endpoints. Authorization logic gets implemented endpoint by endpoint, sprint by sprint, sometimes by different developers. One endpoint gets a proper ownership check. The next one, added in a hurry three months later, gets "we'll add auth later." Later never comes.
"Authorization is not a feature you add. It's a discipline you maintain across every single data access." โ Every post-mortem ever written about a data breach.
The four flavors hunters look for
IDOR isn't just sequential integers in URLs. Here's the taxonomy that separates mediocre recon from high-payout finds:
TypeExampleSeveritySimple integer ID/orders/1024 โ /orders/1023MediumGUID/UUIDPredictable UUIDs from leaked referencesHighHashed IDMD5/SHA1 of email or user ID โ reversibleHighBlind IDORNo visible output, but backend action fires (delete, modify)Critical
Blind IDOR is the most dangerous and most underreported. You send a DELETE request with someone else's document ID. You get back a 204 No Content. You don't see the deleted data โ but it's gone. Programs love these reports because the impact is concrete and severe.
A real hunting methodology
Here's the workflow I use when targeting a new application. Adapt it to your own style, but these steps have consistently produced results:
Hunting checklist
- Create two accounts. attacker@mailinator.com and victim@mailinator.com. This is non-negotiable.
- Log all IDs in Burp Suite. Every numeric value, every UUID, every hash that appears in any response โ log it with the Logger++ extension.
- Map every object type. Orders, invoices, messages, attachments, reports, exports โ list them all.
- Test write operations first. PATCH, DELETE, and PUT are higher severity than GET and easier to miss in code review.
- Test in both directions. Can attacker read victim's data? Can victim read attacker's? Both are valid bugs.
- Try second-order references. Instead of
/user/1042, look for places where a user ID appears nested in another object's response.
The code that causes it
Let's look at vulnerable Node.js code and its fix side by side. Understanding what's wrong in source code sharpens your ability to spot it from the outside.
// โ Vulnerable โ no ownership check
app.get('/api/documents/:id', authenticate, async (req, res) => {
const doc = await db.documents.findById(req.params.id);
if (!doc) return res.status(404).json({ error: 'Not found' });
res.json(doc); // โ returns whatever ID was requested
});
// โ
Fixed โ verify ownership before returning
app.get('/api/documents/:id', authenticate, async (req, res) => {
const doc = await db.documents.findOne({
_id: req.params.id,
owner: req.user.id // โ the only line that matters
});
if (!doc) return res.status(404).json({ error: 'Not found' });
res.json(doc);
});One extra field in the database query. That's it. The vulnerability and the fix are separated by a single line of code โ yet that gap has cost companies millions of dollars in bounty payouts and breach remediation.
Writing the report that gets paid
Finding the bug is half the job. A poorly written report on a critical IDOR can get triaged as "Informational." Here's what triage teams need to see:
Report structure that gets triaged up, not down:
1. One-line summary with the impact, not the technique. 2. Affected endpoint(s) โ exact URL, method, parameter name. 3. Step-by-step reproduction using two accounts โ prove it's not just a coincidence. 4. Evidence of impact โ screenshot or response body showing the victim's real data. 5. Suggested fix โ showing you understand the root cause signals expertise and gets faster response.
What programs actually pay
Bounty amounts depend on data sensitivity and impact. Here's a realistic snapshot based on disclosed reports on HackerOne and Bugcrowd:
Scenario Typical Range Read another user's profile (non-sensitive)$150 โ $500
Read private messages or documents $500 โ $3,000
Expose PII (addresses, SSNs, payment info)$2,000 โ $10,000
Blind IDOR โ delete or modify any user data$ 5,000 โ $25,000+
IDOR leading to account takeover$10,000 โ $50,000+
The mindset shift that finds more bugs
Most beginners look for IDOR in obvious places โ the user profile page, the order history, the invoice download. Those endpoints get tested constantly. The real opportunity is in the long tail: export features, webhook configurations, notification preferences, API keys management pages, audit log exports.
Anywhere the application needs to fetch a specific object belonging to a specific user, there's a potential IDOR. And the less trafficked the feature, the less likely it's been carefully reviewed.
IDOR has been in the OWASP Top 10 for years. Security teams write about it constantly. And yet โ open HackerOne's disclosed reports right now and you'll find one from this week. The vulnerability is old. The opportunity is not.
If you take nothing else from this post, take this: spin up two accounts, intercept your traffic, and change one ID. Do it on every application you test. The payouts speak for themselves.
Happy hunting โ and always stay within scope