Insecure Direct Object Reference happens when the application lets a user access an object by changing its identifier.
The primitive is broken object authorization. The identifier is only the handle. The real bug is that the backend does not verify whether the current user is allowed to access the object behind that handle.
Common object references include:
- User IDs
- Document IDs
- Invoice IDs
- Order IDs
- File names
- Download tokens
- UUIDs
- API paths
- JSON object IDs
- Base64, hash-shaped, or encoded references
Common abuse scenarios include:
- Reading another user's profile
- Downloading another user's documents
- Viewing another customer's invoices
- Modifying another user's settings
- Resetting another user's password
- Accessing admin-only records through a normal user session
- Enumerating API resources with sequential IDs
When enumerating IDOR opportunities, identify:
- Object IDs in URLs
- Object IDs in POST bodies
- Object IDs in JSON fields
- File names passed to download routes
- Hidden form fields containing user IDs or roles
- API routes such as
/api/users/5 - Actions that read, update, delete, approve, export, or download data
- Whether IDs are sequential, encoded, hashed, or UUID-based
- Whether the response changes when using another user's object ID
- Whether horizontal or vertical access is possible
Types
Horizontal IDOR means a normal user accesses another normal user's object.
operator -> alice document
operator -> bob invoice
alice -> bob profile
Vertical IDOR means a lower-privileged user accesses a higher-privileged object.
operator -> admin document
user -> manager report
customer -> support-only ticket metadata
Read IDOR exposes data.
Write IDOR modifies data.
Write impact is usually more serious because the same broken object authorization can change emails, delete files, approve actions, or alter another user's settings.
Enumeration
Use at least two accounts or two seeded users.
operator
alice
bob
admin
Find a request that references an object:
GET /idor/basic?id=2
GET /download?id=2
POST /api/profile {"user_id":2}
PATCH /api/orders/15
Change only the object reference:
GET /idor/basic?id=3
GET /download?id=3
POST /api/profile {"user_id":3}
PATCH /api/orders/16
Compare:
- Status code
- Response length
- Object owner
- Object content
- Error messages
- Redirect behavior
- Response time
Use curl for simple checks:
curl -i "http://127.0.0.1:5000/idor/basic?id=2"
curl -i "http://127.0.0.1:5000/idor/basic?id=3"
Enumerate sequential IDs:
for i in $(seq 1 20); do
echo "[+] id=$i"
curl -s "http://127.0.0.1:5000/idor/basic?id=$i" | grep -Ei "Document ID|Owner|Internal Note"
done
Check the fixed route with the same IDs:
curl -i "http://127.0.0.1:5000/idor/fixed?id=2"
curl -i "http://127.0.0.1:5000/idor/fixed?id=3"
Interesting findings include:
- Another user's data returned with a normal user context
- Admin records returned without an admin role
- Update or delete actions working on another user's object
- Encoded IDs that decode to simple numbers
- Hidden fields controlling
user_id,owner_id, orrole - API endpoints that authorize the session but not the object
- Object IDs visible in JavaScript, HTML comments, or API responses
Once one object is accessible, map the pattern. Determine whether the issue is read-only, write-capable, horizontal, vertical, or chained into another primitive.
Exploit
- Identify the current user's valid object.
GET /idor/basic?id=2
- Change only the object ID.
GET /idor/basic?id=3
-
If the response returns Alice's document, the endpoint has horizontal IDOR.
-
Test vertical impact with an admin-owned object.
GET /idor/basic?id=1
- Stop once impact is proven.
The exploit is not the loop. The exploit is proving that the backend trusts the object reference more than the authorization model.
Remediation
Patch IDOR by checking object access on every object-level operation:
- Authenticate the user.
- Load the target object server-side.
- Verify the current user can access that object.
- Enforce ownership or role rules in the query.
- Deny by default when ownership cannot be proven.
- Apply the same check to read, update, delete, download, export, and admin actions.
Safe document lookup:
SELECT id, owner_id, title, path
FROM documents
WHERE id = ?
AND owner_id = ?;
When admin access is valid, include the role rule explicitly:
SELECT id, owner_id, title, path
FROM documents
WHERE id = ?
AND (owner_id = ? OR ? = 'admin');
UUIDs and random tokens reduce easy guessing. They do not replace authorization.