Skip to content

IDOR

IDOR is one bug masquerading as two: the app exposes a direct handle to a resource (?file_id=123, /api/profile/74, ?contract=<md5>), and the back-end doesn’t check whether the caller is authorized to use that handle. Substitute someone else’s identifier and you get their data - or perform actions in their name.

# Identify a direct reference in a URL, body, or header
?file_id=123 /api/profile/74 Cookie: uid=1 contract=cdd9...
# Substitute and observe
curl 'http://target/download.php?file_id=124' # someone else's file?
curl 'http://target/api/profile/75' # someone else's profile?
# Mass-enumerate when the pattern is sequential
for i in $(seq 1 1000); do
curl -s "http://target/api/profile/$i" >> all-profiles.json
done
# When the handle is hashed or encoded, find the algorithm
echo -n '1' | base64 | md5sum # often the answer is in the front-end JS

Success indicator: a resource (file, record, action result) you should not have access to comes back as if it were yours.

Two ingredients, both required:

  1. Direct object reference - a handle in a request that names a specific resource (file, record, account, action). The handle is in client-controlled space: URL parameter, path segment, header, cookie, body field.
  2. Missing access control - the server treats the handle as authoritative, looking up the resource without first checking “is the caller allowed to touch this resource?”

The “Insecure” in IDOR modifies the access control, not the reference. Direct object references are normal and necessary - the bug is the missing authorization layer. A perfectly secure app can use sequential integer IDs in its URLs, provided every request hits an access-control check that compares the caller’s identity to the resource’s ACL.

Apps fail this in three common ways:

FailureExample
No access control at all/api/profile/74 returns user 74’s data to anyone who asks
Access control only in the UIThe UI hides the “edit other users” button; the back-end happily processes the request if you send it
Access control only on some verbsGET /api/profile/74 checks; PUT /api/profile/74 doesn’t - see Verb tampering

The operator should distinguish these because the impact and the exploitation workflow are different:

FlavorWhat it doesOperator workflow
Information disclosureRead resources you shouldn’t see - files, records, profile fields, internal stateSubstitute the reference, dump the response, iterate to enumerate everything
Insecure function callPerform actions you shouldn’t be able to - change someone else’s password, delete records, escalate roleSubstitute the reference and construct a request to the action endpoint; chain with disclosure to learn required parameters (other user’s uuid, role names, etc.)

The two often chain: disclosure leaks the values you need (the victim’s uuid, the admin role name, the file hash format) to invoke the function call.

Look in this order - the first three account for most findings:

LocationExampleNotes
URL query?uid=1, ?file_id=123Most common; visible in browser; easiest to fuzz
URL path/users/74, /files/abc123.pdfREST APIs favor this; same risk
Request body{"uid": 74, "uuid": "40f5..."}API mutations; check both the JSON keys and hidden form fields
HeaderCookie: uid=1, X-User-Id: 1, Authorization: Bearer ...Sometimes the only auth signal; the bearer token, if not bound to the resource, lets any logged-in user read anyone’s data
AJAX call in JSfetch('/api/admin/users') triggered only when role=adminRead the front-end JS - find function-call references the server might accept from any session

The front-end JS angle deserves emphasis. Many SPAs put the whole function catalog in JS and gate each call by role on the client. Read the JS, find the admin-only function:

function changeUserPassword() {
$.ajax({
url: "change_password.php",
type: "post",
data: {uid: user.uid, password: user.password, is_admin: is_admin},
...
});
}

The JS only invokes this when the user is admin. But the back-end endpoint accepts requests from anyone - that’s the IDOR. The discovery method is reading the bundle, not poking the UI.

Building an access-control system is a separate cross-cutting concern that has to be designed in from the start. RBAC (role-based access control), ABAC (attribute-based), or per-row ACLs are non-trivial to retrofit. Apps that grow organically tend to accumulate authentication (knowing who you are) without authorization (knowing what you’re allowed to do at the resource level).

The result: IDOR is one of the most common findings in modern web apps, including Facebook, Instagram, and Twitter.

SeverityExample
LowDisclosure of public-ish data (other users’ display names, profile pictures already shared elsewhere)
MediumDisclosure of internal data (other users’ email addresses, internal IDs, account creation dates)
HighDisclosure of sensitive personal data (credit cards, SSNs, addresses, private messages, uploaded documents)
CriticalAccount takeover (change another user’s password, email, recovery token); privilege escalation (set your own role to admin); mass data manipulation

The “Critical” tier is where chained IDORs land. Information-disclosure IDOR + function-call IDOR + role-name leak together = unauthenticated admin compromise.

PageFocus
IdentifyingWhere to find direct object references, AJAX-call discovery, hash/encoding inspection, comparing roles across test accounts
Mass enumerationBurp Intruder / ffuf / scripted bulk pulls; reversing MD5-of-base64-style “hashed” references
Insecure APIsREST verb-by-verb attack surface (PUT/POST/DELETE on user objects), uid-mismatch / uuid-mismatch defense bypass, role-field manipulation
ChainingEnd-to-end: GET disclosure → uuid harvest → PUT modification → role escalation → admin function calls
TopicPage
Verb-tampering bypasses (auth scoped to GET/POST only)Verb tampering
Session attacks that often follow IDOR disclosureSessions cluster
When a leaked file URL points to disk contentLFI cluster
XSS payloads delivered via IDOR-modified profile fieldsXSS to session
Password reset flow chained with IDOR-modified emailReset tokens
XXE for back-end file disclosure after IDOR footholdXXE cluster
Defenses D3-IAA