Insecure Direct Object Reference (IDOR) is consistently one of the highest-impact vulnerabilities in web applications and APIs — and it’s one of the easiest to miss during a standard pentest if you don’t know where to look.
It doesn’t require complex payloads. No memory corruption. No custom shellcode. You find an ID, swap it for another ID, and suddenly you’re reading someone else’s data. Or deleting it. Or taking over their account.
This guide covers everything you need to find and exploit IDOR in 2026: detection methodology, exploitation techniques, automation tools, WAF bypass, and how to chain IDOR with other vulnerabilities for maximum impact.
What Is IDOR?
Insecure Direct Object Reference occurs when an application uses user-controllable input to access objects (database records, files, functions) directly — without verifying that the requesting user is actually authorized to access that specific object.
The simplest example:
GET /api/v1/invoices/1042
You’re user ID 55. Invoice 1042 belongs to user ID 89. If the application returns it anyway — that’s IDOR.
The vulnerability isn’t in the ID being exposed. It’s in the missing authorization check. The server trusts that if you know the ID, you’re allowed to see it. That assumption is wrong.
IDOR falls under Broken Access Control in the OWASP Top 10 — the #1 category since 2021 — and it’s the root cause of some of the most damaging data breaches of the past decade.
Why IDOR Still Dominates Bug Bounties in 2026
Three reasons IDOR is everywhere:
Developers assume obscurity = security. UUIDs are “random enough,” they think. GUIDs are “unpredictable.” Except they test with their own account and never try another user’s ID.
Authorization is hard to test systematically. Static analysis tools are terrible at finding IDOR. You need multiple accounts with different permission levels and you need to actively probe.
Modern APIs explode the attack surface. Every REST endpoint that accepts an ID is a potential IDOR. A typical SaaS app has hundreds of these. Most are never manually tested.
Bug bounty programs pay well for IDOR because the business impact is real: PII exposure, data exfiltration, account takeover, privilege escalation. P1/critical findings on HackerOne and Bugcrowd are frequently IDOR.
IDOR vs BOLA: What’s the Difference?
BOLA (Broken Object Level Authorization) is OWASP API Security’s name for the same thing — IDOR in the context of APIs. If you see BOLA referenced in API security docs, it’s IDOR.
The distinction matters only for communication:
- Use IDOR when talking about traditional web apps
- Use BOLA in API security reports and conversations
- Functionally identical vulnerability class
Types of IDOR Vulnerabilities
1. Sequential Integer IDs
The classic. User IDs, order IDs, invoice numbers, document IDs — sequential integers that increment predictably.
/api/orders/10041
/api/orders/10042 ← yours
/api/orders/10043
Swap 10042 for any other number. If the server doesn’t verify ownership, you can enumerate every order in the system.
2. Encoded IDs (Base64, Hex)
Applications sometimes encode IDs to make them look “safer”:
/profile?id=MTAwNDI=
Decode that base64 → 10042. Same IDOR, one decode step away.
import base64
base64.b64decode("MTAwNDI=") # b'10042'
Look for strings that end in = or == in parameters. Always try base64 decode first.
3. GUID/UUID-Based IDOR
GUIDs look random but aren’t necessarily:
/api/documents/550e8400-e29b-41d4-a716-446655440000
GUIDs generated with weak entropy or predictable seeds can be brute-forced. More importantly, GUIDs often leak — in API responses, email notifications, URL redirects, referrer headers. Find another user’s GUID through a different endpoint and use it directly.
4. Multi-Step IDOR
The vulnerable operation isn’t on the first request — it’s in a workflow:
POST /cart/add→ adds item to cartPOST /cart/checkout→ processes paymentGET /orders/{order_id}→ this is where IDOR lives
Step 3 returns another user’s order if you guess their order ID. Multi-step IDOR is frequently missed because testers focus on the obvious endpoints.
5. Parameter Pollution IDOR
Applications that accept JSON or form data sometimes process hidden parameters:
POST /api/profile/update
{
"name": "John",
"email": "[email protected]",
"user_id": 9999
}
The user_id field isn’t displayed in the UI but the server processes it. Adding it to the request changes whose profile gets updated. Classic mass assignment / parameter pollution leading to IDOR.
6. File Path IDOR
File downloads with user-controlled paths:
GET /download?file=reports/user_1042/q3-report.pdf
Change 1042 to another user ID. Or try path traversal:
GET /download?file=../../../etc/passwd
This is IDOR + path traversal combined — common in export/download functionality.
7. Indirect IDOR via References
Not all IDOR is direct. Some applications use references that map to objects:
GET /api/messages?thread_id=abc123
abc123 is a thread ID that belongs to another user’s conversation. If the mapping isn’t authorization-checked server-side, you can read any thread.
IDOR Detection: Where to Look
Start every web app pentest with a systematic scan for object references. Here’s where to look:
URL Parameters
/users/1042/settings
/invoices/view?id=9847
/admin/delete?record=334
JSON Request Bodies
{"user_id": 1042, "action": "get_profile"}
{"document_id": "abc123", "format": "pdf"}
{"account": {"id": 9847}}
Headers
X-User-ID: 1042
X-Resource-Token: abc123
Authorization: Bearer <JWT with sub claim>
JWTs are common — decode the payload and check if any fields map to objects you can manipulate.
Hidden Form Fields
<input type="hidden" name="user_id" value="1042">
<input type="hidden" name="order_ref" value="ORD-9847">
Burp Suite’s passive scan will highlight these. Always check hidden fields.
API Responses That Leak IDs
Responses often leak IDs you can then use in other requests:
{
"user": {"id": 1042, "email": "[email protected]"},
"created_by": 89, ← another user's ID
"assigned_to": 331, ← another user's ID
"org_id": 7 ← organization-level reference
}
Collect all IDs from every response. Test each one against every endpoint.
Manual IDOR Testing Methodology
Step 1: Set Up Two Accounts
You need at minimum two accounts at the same privilege level. Create:
- Account A (your primary testing account)
- Account B (your “victim” account)
For higher-severity IDOR, also create:
- Admin account (if your scope includes privilege escalation)
Step 2: Map All Object References
Browse the application as Account A. Use Burp Suite to intercept everything. Build a list of every ID/reference you see in:
- URL paths
- Query parameters
- Request bodies
- Response bodies
Step 3: Generate Account B Data
Log in as Account B and create the same types of objects (invoices, orders, documents, messages). Note the IDs assigned to B’s objects.
Step 4: Test as Account A, With B’s References
Log back in as Account A. Take every endpoint that accepts an object reference and replace A’s reference with B’s:
GET /api/invoices/A_INVOICE_ID → 200 OK (expected)
GET /api/invoices/B_INVOICE_ID → should be 403, test if 200
Document every endpoint and result.
Step 5: Test State-Changing Operations
Read-only IDOR is P3. Write/delete IDOR is P1. Test:
PUT /api/orders/B_ORDER_ID (as Account A)
DELETE /api/messages/B_MESSAGE_ID (as Account A)
POST /api/transfer?from=B_ACCOUNT (as Account A)
Step 6: Test Privilege Escalation
Use B as a low-privilege account. Test whether A (also low-privilege) can access admin-only resources:
GET /api/admin/users/list (as Account A)
PUT /api/users/B_USER_ID/role {"role": "admin"} (as Account A)
Automated IDOR Discovery
Manual testing covers known endpoints. Automation covers the surface you’d otherwise miss.
Burp Suite — Authorize Extension
Authorize is the go-to Burp extension for IDOR testing. It intercepts every request from Account A and automatically replays it using Account B’s session token. Differences in response indicate potential IDOR.
Setup:
- Install Authorize from BApp Store
- Log in as Account B, copy the session cookie/auth header
- Paste into Authorize’s “Auth Header” field
- Browse the app as Account A
- Authorize replays each request as B — look for
200 OKresponses where B shouldn’t have access
Param Miner
Burp extension that discovers hidden parameters not visible in the UI. Often uncovers user_id, account_id, role parameters that the application processes but doesn’t expose to the frontend.
Extensions → Param Miner → Right-click request → Guess params
ffuf for IDOR Enumeration
Once you find an IDOR, fuzz the full range:
ffuf -u "https://target.com/api/invoices/FUZZ" \
-H "Cookie: session=YOUR_COOKIE" \
-w /usr/share/wordlists/numbers.txt \
-mc 200 \
-o idor_results.json
Generate a wordlist of sequential integers:
seq 1 100000 > /tmp/numbers.txt
Nuclei — IDOR Templates
Nuclei has community templates for common IDOR patterns:
nuclei -u https://target.com \
-t ~/nuclei-templates/vulnerabilities/generic/idor.yaml \
-H "Cookie: session=YOUR_COOKIE"
Custom Python Script
For API-heavy targets, a quick script beats manual swapping:
import requests
BASE_URL = "https://api.target.com"
HEADERS_A = {"Authorization": "Bearer TOKEN_A"}
HEADERS_B = {"Authorization": "Bearer TOKEN_B"}
# Get B's resource IDs
b_resources = requests.get(f"{BASE_URL}/api/invoices", headers=HEADERS_B).json()
b_ids = [r["id"] for r in b_resources["invoices"]]
# Test each as A
for resource_id in b_ids:
r = requests.get(f"{BASE_URL}/api/invoices/{resource_id}", headers=HEADERS_A)
if r.status_code == 200:
print(f"[IDOR] {resource_id} accessible by Account A → {r.text[:100]}")
Exploitation Techniques
Horizontal Privilege Escalation
Most common: access another user’s data at the same privilege level.
Impact depends on what’s exposed:
- Financial data (invoices, payment methods): P1-P2
- PII (profile, addresses, SSN): P1
- Messages/communications: P2
- Generic app data (preferences, settings): P3-P4
Document the full scope of exposure. If you can read 1 record, you can enumerate all records. Calculate the blast radius.
Vertical Privilege Escalation via IDOR
Higher severity: use IDOR to escalate privileges.
Scenario 1 — Role modification:
PUT /api/users/YOUR_USER_ID
{"role": "admin", "name": "Your Name"}
If the role field isn’t filtered server-side and you can modify your own user object, you’ve escalated to admin.
Scenario 2 — Admin object access:
GET /api/admin/audit-logs?user=1 (as low-privilege user)
No privilege check on an “admin-only” endpoint — IDOR grants access.
Scenario 3 — Function-level IDOR:
POST /api/admin/create-user (as regular user)
The endpoint exists and processes requests regardless of the caller’s role. This is IDOR combined with Broken Function Level Authorization.
IDOR Chains
The real damage comes from chaining IDOR with other vulnerabilities:
IDOR → Account Takeover:
- IDOR on
/api/users/{id}/email— you can read any user’s email - Use the email to trigger password reset
- IDOR on
/api/password-reset-tokens/{token}— you can read the reset token (if it’s stored as an object reference) - Use token to reset victim’s password → account takeover
IDOR → SSRF:
- Application has a webhook feature:
POST /api/webhooks/{id}/test - IDOR lets you trigger another user’s webhook configuration
- Webhook URL points to internal services
- You can now fire SSRF requests via another user’s webhook
IDOR → Data Exfiltration at Scale:
- IDOR on
/api/export/{job_id}/download - Export jobs contain full data dumps
- Enumerate job IDs → download other users’ complete data exports
WAF Bypass for IDOR
WAFs can’t prevent IDOR — they block malicious input patterns, and IDOR requires only a valid ID. There’s nothing malicious in the payload itself. A WAF that blocks SQL injection won’t block GET /api/invoices/1043 when you should only have access to 1042.
That said, some applications implement custom WAF rules or rate limiting that may interfere with IDOR enumeration:
Rate Limit Bypass
Distributed requests across multiple source IPs:
ffuf -u "https://target.com/api/invoices/FUZZ" \
-H "Cookie: session=COOKIE" \
-w numbers.txt \
-rate 10 \
-p 0.1-0.3
Or rotate through headers:
X-Forwarded-For: 1.2.3.4
X-Real-IP: 1.2.3.5
True-Client-IP: 1.2.3.6
ID Encoding Variations
If direct integer enumeration is blocked, try encoded forms:
/api/invoices/1043 → try
/api/invoices/MTAzNA== → base64
/api/invoices/0x40B → hex
/api/invoices/1.043e3 → scientific notation
Parameter Location Variation
If the endpoint blocks ID modification in one location, try another:
GET /api/invoices?id=1043 → blocked
POST /api/invoices {"id": "1043"} → try
GET /api/invoices/me?override_id=1043 → try
Indirect Reference Enumeration
If direct ID enumeration triggers rate limiting, find the ID through a different endpoint first:
GET /api/[email protected] → returns user_id
GET /api/users/{leaked_user_id}/profile → IDOR with specific target, no bruteforce
Real-World IDOR Examples
Facebook IDOR — User Posts Deletion (2015)
Researcher Laxman Muthiyah found that authenticated users could delete any other user’s posts by modifying the post_id parameter. Affected every Facebook account. Bounty: $12,500.
Uber IDOR — Driver Account Takeover (2016)
IDOR in the driver activation flow allowed attackers to intercept activation tokens and take over driver accounts. The exploit chain combined IDOR with a token leak in the API response.
US Department of Defense — IDOR Data Exposure (2019)
HackerOne public disclosure: an IDOR on a DoD contractor portal exposed PII of thousands of government employees. Sequential integer IDs on profile endpoints, no authorization check.
Shopify Partner Dashboard IDOR (2020)
A researcher discovered that Shopify’s partner API accepted partner IDs without ownership verification. An attacker with any partner account could read analytics, orders, and customer data for any other partner’s stores.
Modern API IDOR Patterns (2025-2026)
The current trend: BOLA in GraphQL. GraphQL queries accept object IDs inline:
query {
order(id: "ord_1043") {
total
customer { email, address }
payment { last4, expiry }
}
}
No introspection disabled + missing authorization checks = full data model exposed to IDOR. GraphQL BOLA findings dominated bug bounty programs in 2025.
Practice Labs
The best way to learn IDOR is hands-on. These labs give you safe environments to practice:
DVWA (Damn Vulnerable Web Application)
# Spin up a disposable VPS for practice
# Vultr Basic: $6/month, deploy Ubuntu 22.04
# https://www.vultr.com/?ref=9888008
docker run --rm -p 80:80 vulnerables/web-dvwa
DVWA’s IDOR module covers basic ID enumeration. Set difficulty to Medium/High to practice bypass techniques.
WebGoat (OWASP)
docker run -p 8080:8080 -t webgoat/webgoat
WebGoat has dedicated IDOR lessons including:
- Insecure Direct Object References
- Missing Function Level Access Control
- Indirect Object Reference
PortSwigger Web Security Academy
Free, browser-based labs with increasing difficulty:
- Insecure direct object references
- Multistep IDOR
- IDOR with JSON parameter tampering
No infrastructure needed — everything runs in the browser.
PentesterLab
PentesterLab’s Pro version has IDOR-specific exercises including API BOLA scenarios. Good for certification prep (PCAP, BSCP).
Bug Bounty Practice
HackerOne has public programs with IDOR-friendly scope. Start with:
- Programs marked “Good for Beginners”
- SaaS platforms with multiple user account types
- APIs that expose object references in responses
A $6/month VPS on Vultr or DigitalOcean is enough to run multiple vulnerable Docker containers simultaneously and practice the full enumeration workflow.
Tools Summary
| Tool | Use Case |
|---|---|
| Burp Suite Pro | Primary interception, Authorize extension, Param Miner |
| Authorize (Burp) | Automated multi-session IDOR testing |
| Param Miner (Burp) | Hidden parameter discovery |
| ffuf | IDOR enumeration / ID fuzzing |
| Nuclei | Automated IDOR template scanning |
| OWASP ZAP | Free alternative to Burp for IDOR testing |
| Postman/Insomnia | Manual API IDOR testing |
| jwt_tool | JWT claim manipulation for IDOR via token |
IDOR Testing Checklist
Before closing out any web app engagement, run through this:
- Enumerated all endpoints accepting object IDs (URL, body, headers)
- Tested with two accounts at the same privilege level (horizontal)
- Tested with accounts at different privilege levels (vertical)
- Tested all HTTP methods: GET, POST, PUT, PATCH, DELETE
- Checked hidden form fields and JSON body parameters for references
- Decoded and tested encoded IDs (base64, hex, JWT claims)
- Tested all export/download functionality
- Checked API response bodies for leaked IDs from other users
- Attempted IDOR chains (IDOR → ATO, IDOR → privilege escalation)
- Documented blast radius (how many records are exposed)
Summary
IDOR is simple in concept, brutal in impact, and still present in the majority of web applications and APIs tested in 2026. It requires no special tooling, no exotic payloads — just systematic testing with two accounts and attention to every object reference the application exposes.
The methodology:
- Map all object references across the application
- Test access with a second account at the same privilege level
- Test state-changing operations (write, delete, modify)
- Look for privilege escalation opportunities
- Chain with other vulnerabilities for maximum impact
If you’re doing bug bounty or red team assessments, IDOR should be one of the first things you test systematically — before you spend hours on XSS or complex injection chains. The payoff-to-effort ratio is hard to beat.
Want pentest reports and red team deliverables that actually look professional? CipherWrite handles technical writing for security teams — reports, whitepapers, and client-facing documentation.
