IDOR vulnerability and broken access control illustration showing protected and blocked object access.

IDOR Vulnerability: 7 Sneaky Access Control Flaws

An IDOR vulnerability happens when an application accepts an object reference such as a user ID, invoice number, filename, UUID, or API record identifier but fails to verify that the current user is actually allowed to access that object. The result is a broken access control flaw: change the reference, and the server may reveal or modify somebody else’s data. In this guide I break down 7 sneaky access control flaws, show how I would test them safely in a lab, and explain what developers should fix on the server side.

The important bit is that an IDOR vulnerability is not “a predictable number problem.” Predictable IDs make testing easier, but OWASP is clear that the real failure is missing object-level authorization. A long UUID does not become permission just because it looks unpleasant to type.

I also keep this firmly on the ethical side: use these checks only on applications you own, intentionally vulnerable training targets, or systems you have explicit permission to test. You do not need a stranger’s customer portal to understand how insecure direct object reference works. A lab with two accounts is more useful and considerably less likely to ruin your afternoon.

What changesWhat should happenPossible IDOR vulnerability signal
Object ID or filenameServer rechecks ownershipAnother user’s object appears
HTTP methodAuthorization applies againRead blocked, update allowed
Endpoint or API versionSame policy everywhereOlder route skips the check
Export or download requestObject-level check before file accessDirect file becomes accessible

Key Takeaways

  • An IDOR vulnerability is an authorization failure. Guessable identifiers are useful clues, not the root cause.
  • Test with two controlled accounts. User A should never gain User B’s object simply by changing a reference.
  • Check every operation. Read, edit, delete, download, export, and API actions can enforce authorization differently.
  • UUIDs are defense in depth, not a fix. If an unauthorized user gets a valid UUID, the server must still deny access.
  • The repair belongs on the server. Hidden buttons, disabled form fields, and JavaScript checks do not replace object-level authorization.

Access-control testing often means juggling multiple lab accounts. I keep those credentials separate from production accounts and from one another. If you want a password manager for lab and admin credentials, NordPass can generate and store unique passwords and secure notes. It will not fix an IDOR vulnerability; it simply helps keep credential reuse out of the test setup. Affiliate disclosure: I may earn a commission if you buy through these links, at no extra cost to you.

What an IDOR Vulnerability Actually Is

An IDOR vulnerability appears when an application exposes a reference to an object and trusts that reference without correctly authorizing the request. The object might be a profile, invoice, support ticket, project, private image, downloadable file, shopping order, API record, or anything else the application identifies individually.

A simple training example looks like this:

GET /account/invoices/1042

If User A owns invoice 1042, the application should verify that relationship before returning it. If User A changes the request to 1043 and receives User B’s invoice, the problem is not that 1043 was easy to guess. The problem is that the server found the object but skipped the authorization decision.

That makes an IDOR vulnerability a form of broken access control. OWASP currently places Broken Access Control at A01 in its Top 10 and also treats broken object-level authorization as a major API security risk. That distinction is useful because it keeps the fix in the right place: server-side authorization, not cosmetic identifier changes.

HackersGhost Note:
What I usually check first is whether the application answers two separate questions: “Does this object exist?” and “May this user access it?” Finding a database row is not an authorization decision. If those two ideas have quietly merged into one, I start looking for an IDOR vulnerability.

Object-level authorization illustration showing allowed and blocked access to user records.

1. Sequential IDs Expose Missing Object Authorization

The classic IDOR vulnerability example uses numbers such as 101, 102, and 103. Sequential IDs are easy to notice because the relationship between requests is obvious. That makes them excellent for learning, but they are not inherently insecure.

In my own lab, I would create two normal user accounts and give each one a separate object: perhaps a support ticket or saved document. I authenticate as User A, capture User A’s request, then replace only the object reference with the identifier belonging to User B. I am not fuzzing the internet or enumerating a production database. I already know both objects because I created them.

The secure result of an IDOR vulnerability test is boring, which is exactly what I want: 403 Forbidden, 404 Not Found, or another controlled denial depending on the application’s design. The insecure result is User B’s object appearing in User A’s session.

Why changing the ID is only the test

The changed number demonstrates the flaw; it does not cause it. A server can safely use sequential IDs if every request is authorized against the current user, role, tenant, or other policy context. Likewise, replacing 1042 with a random UUID does not cure an IDOR vulnerability if the application still returns any valid object presented to it.

Burp Suite Proxy Tutorial: Intercept in 7 Easy Steps

Learn how to capture and compare your own lab requests before testing authorization decisions.

2. Hidden Form Fields Can Carry an IDOR Vulnerability

An object reference does not have to appear in the URL. It can live in a hidden form field, JSON body, GraphQL variable, cookie, request header, or multipart upload field. If the browser sends it, the user can usually modify it.

{
  "profile_id": "user-a-profile",
  "display_name": "Robin"
}

If the API updates whichever profile_id arrives without checking ownership, changing that value can reveal an insecure direct object reference. A hidden input is still client-controlled data. The word “hidden” describes the interface, not the trust level.

This is one reason I like inspecting complete requests instead of staring only at the address bar. A web page may look static while the browser quietly sends object references through background API calls. The interesting authorization boundary often lives behind the interface.

HackersGhost Note:
One thing I learned while testing web apps is that the browser UI is a suggestion; the request is the evidence. Disabled buttons, hidden inputs, and greyed-out options can make a page look restricted while the server remains far too optimistic.

3. Read Access Is Blocked but Update or Delete Is Not

A particularly sneaky IDOR vulnerability appears when authorization is implemented for one HTTP method but forgotten for another. A developer may protect GET /documents/42 correctly while PUT /documents/42 or DELETE /documents/42 reaches a different controller with weaker checks.

That means a safe test should not stop after one response. For each object you control in your lab, check the operations that the application legitimately exposes: read, update, delete, download, share, export, archive, restore, and any business-specific action.

I do not try random destructive methods against somebody else’s service. In a controlled lab, I create disposable objects precisely so I can verify what happens without damaging anything important. If User A can delete User B’s test object while being unable to view it, the access control model has become impressively inconsistent.

Access control illustration showing different authorization outcomes for actions on the same object.

4. Download and Export Endpoints Skip the Main Check

Applications often have several ways to reach the same data. A customer might view an invoice through the dashboard, download a PDF, export a CSV, email a copy, or fetch the same record through an API. The main page can enforce authorization perfectly while a secondary route creates the IDOR vulnerability.

A lab checklist I find useful is to map one object to every route that touches it. If a ticket can be viewed at /tickets/42, ask what handles its attachment, print view, API representation, and export. The goal is not to collect endpoints for sport. It is to verify that the same authorization policy follows the object everywhere.

Direct file URLs deserve special attention. A filename can itself be an object reference. OWASP’s IDOR guidance explicitly notes that references may be filenames, account numbers, tokens, slugs, UUIDs, or other values—not just numeric database keys.

OWASP Juice Shop Walkthrough: 7 Safe Beginner Challenges

Use intentionally vulnerable applications when you want hands-on practice without inventing your own production target.

5. UUIDs Hide the IDOR Vulnerability Instead of Fixing It

Replacing 42 with something like 7f3c... makes enumeration harder. That is useful defense in depth, but it is not object authorization. If User A receives a link to User B’s UUID through a log, browser history, referrer, shared message, API response, analytics event, or another leak, the server must still reject the request.

This is where IDOR vulnerability discussions sometimes drift into “unguessable equals secure.” It does not. OWASP specifically recommends complex identifiers only as an additional layer while keeping access checks mandatory.

In a controlled test, I therefore avoid spending time brute-forcing UUIDs. I already know the second account’s object ID. I take that known valid reference and ask the authorization question directly: can User A use User B’s valid identifier? If yes, the random format merely decorated the bug.

IDOR Vulnerability vs BOLA in APIs

API documentation often uses the term Broken Object Level Authorization (BOLA). The idea overlaps heavily with an IDOR vulnerability: an API receives an object identifier but fails to verify whether the authenticated caller may perform that action on that specific object. OWASP’s API Security project places BOLA at the top of its API risk list because object identifiers are everywhere in modern APIs.

The terminology matters less than the test. When an endpoint accepts an ID, UUID, slug, or other object selector, the server should authorize the operation against the current user and business rules before returning or changing anything.

6. Multi-Tenant Apps Check the Role but Not the Tenant

Some of the most serious broken access control problems appear in multi-tenant software. A user may legitimately have the role “manager,” but that role should only apply inside their own company, project, workspace, school, customer account, or other tenant boundary.

Imagine User A is a manager for Tenant A. The server checks role === manager and then loads invoice 9001. If invoice 9001 belongs to Tenant B and no tenant ownership check follows, the role check is technically present and practically useless.

This is why I prefer a test matrix rather than one “admin vs user” comparison. In a lab, I would create:

  • User A, ordinary member of Tenant A
  • User B, ordinary member of Tenant B
  • Manager A, elevated role inside Tenant A
  • Manager B, elevated role inside Tenant B

Then I verify horizontal access between users and cross-tenant access between roles. An IDOR vulnerability can hide behind a correct role check when the application forgets the object’s organization, owner, project, or tenant relationship.

HackersGhost Note:
Access control gets interesting when “who are you?” is answered correctly but “which data belongs to your world?” is not. Authentication can be perfect and the application can still hand the wrong tenant the right database row.

Multi-tenant access control illustration showing an unauthorized cross-tenant request being blocked.

7. Old or Alternate Endpoints Keep Broken Access Control Alive

An application can fix an IDOR vulnerability in the shiny new interface while an older API route, mobile endpoint, legacy parameter, or alternate content type still reaches the object through weaker authorization logic.

This is one reason regression testing matters. Access control is not a feature you test once and frame on the wall. New controllers, API versions, data-layer refactors, and mobile clients can quietly reintroduce differences.

In a legal lab or application I own, I compare equivalent functions rather than spraying random endpoints. If the current UI retrieves an object through /api/v2/documents/42 and an older client still uses /api/v1/documents/42, both should enforce the same policy. The same goes for JSON versus form submissions, browser routes versus API routes, and “preview” versus “download” paths.

OWASP’s current authorization regression guidance makes this point directly: authorization logic changes as applications evolve, so automated checks should verify that known boundaries remain intact. That turns an IDOR vulnerability from a one-time penetration-testing surprise into something development teams can catch continuously.

How to Use Burp Suite Without 7 Common Beginner Mistakes

Keep request interception controlled and understandable while comparing authorization behavior.

How I Test an IDOR Vulnerability Safely

I like a two-account workflow because it answers the authorization question without unnecessary enumeration. The method is simple enough for beginners and still mirrors how professional testers reason about object-level access.

Step 1: Create two accounts you control

User A and User B should have the same normal role unless the test is specifically about privilege levels. Give each account its own objects: two tickets, two orders, two private notes, or similar lab data. I also give the accounts distinct credentials; NordPass password manager is one option for keeping lab logins and secure notes separate. That helps credential hygiene, but it does not replace server-side authorization.

Step 2: Record the legitimate requests

Use the browser developer tools or an intercepting proxy such as Burp Suite to observe the requests created by each account. I record the endpoint, method, object reference, account in use, and expected response. This makes the test reproducible instead of becoming “I changed some number and something weird happened.”

Step 3: Replay one known cross-account reference

Authenticate as User A and replace User A’s object reference with the known reference belonging to User B. Keep everything else the same. Do not automate broad enumeration unless your written scope explicitly permits it and you actually need it.

Step 4: Compare the response, not only the status code

A 200 OK with sanitized or empty data may be secure; a 404 can be an intentional authorization response. Look at the body, headers, state changes, and what the application actually did. If User A can read, update, download, or delete User B’s object, you have evidence of an IDOR vulnerability.

Step 5: Repeat across the legitimate operations

Check the actions the application exposes for that object. I am looking for inconsistent policy enforcement, not trying to invent every HTTP verb in the dictionary. If the app lets users view, edit, export, archive, or share something, those are the operations worth testing.

The OWASP Foundation is my first external reference for the underlying access-control model, while PortSwigger provides practical web-security learning material and tooling around HTTP request analysis. I keep the article itself focused on the reasoning rather than turning it into a random payload catalogue.

How Developers Fix an IDOR Vulnerability

The correct fix is usually straightforward to describe and application-specific to implement: authorize the current user for the requested object on every relevant server-side operation.

A vulnerable pattern conceptually looks like this:

object = findObject(request.objectId)
return object

The server finds a valid object and immediately returns it. A safer pattern scopes the lookup or authorization decision to what the current user is permitted to access:

object = findObjectAllowedFor(currentUser, request.objectId)

if object is not allowed:
    deny access

return object

The exact framework syntax varies, but the principle does not. OWASP recommends using the authenticated session context and restricting object lookups to the user’s authorized dataset where practical. That can make the secure path harder to forget because unauthorized objects never appear in the query result in the first place.

Do not trust client-side restrictions

Hiding a button, disabling an input, removing an ID from the page, or checking permissions in JavaScript may improve the interface, but the request can still be modified. The IDOR vulnerability exists on the server, so the decisive authorization check also belongs on the server.

Use UUIDs only as an extra layer

Random identifiers can reduce easy enumeration and are often a sensible design choice. They should sit on top of authorization, not instead of it. If the application receives a valid reference for an object the caller does not own, the response still needs to be denied.

Centralize authorization where possible

The more endpoints independently reinvent the same rule, the easier it is for one to forget it. Framework policies, middleware, service-layer authorization, scoped repositories, and reusable permission checks can reduce inconsistent behavior. Business rules still need careful design, but centralization makes them easier to review and regression-test.

Add authorization regression tests

For every sensitive object type, create tests that prove one user cannot access another user’s records and that tenant boundaries survive new releases. A tiny automated test that fails during development is much cheaper than rediscovering the same IDOR vulnerability during every security review.

Server-side authorization illustration showing a request checked before database object access.

My IDOR Vulnerability Checklist

  • Two accounts: can User A access a known object owned by User B?
  • All object references: URL, query string, JSON, form fields, filenames, headers, tokens, slugs, and UUIDs.
  • All legitimate operations: read, edit, delete, download, export, share, archive, and restore.
  • Tenant boundaries: does a role remain limited to the correct organization or workspace?
  • Alternate routes: API versions, mobile routes, print views, direct downloads, and background endpoints.
  • Server-side enforcement: does authorization happen after authentication and before object access?
  • Regression coverage: will future changes automatically retest the same boundary?

If credential hygiene is part of your follow-up, NordPass Premium can help keep unique lab, admin, and recovery credentials organised. That supports the access-control work without pretending a password manager can replace object-level authorization.

HackersGhost Final Note:
An IDOR vulnerability is wonderfully boring once you strip away the acronym. The server has an object, the user asks for it, and the application forgets to ask whether the user is allowed to have it. Good testing makes that missing question visible; good engineering makes sure the question is asked every time.

IDOR Vulnerability FAQ

What is an IDOR vulnerability?

Is an IDOR vulnerability the same as broken access control?

What is an insecure direct object reference example?

Do UUIDs prevent an IDOR vulnerability?

How do you test for an IDOR vulnerability safely?

What is the difference between IDOR and BOLA?

Can an IDOR vulnerability allow data modification?

How should developers fix an IDOR vulnerability?

ⓘ

Some links in this article are affiliate links. If you use them, I may earn a small commission — at no extra cost to you. I only recommend tools I’ve actually tested inside my own cybersecurity lab. Read the full disclaimer.

In many cases, these links unlock better deals than you’ll find on your own.
No paid reviews. No sponsored opinions. Just real testing and real setups.

If you decide to use them, you’re not just getting a discount — you’re helping keep this lab running.

Leave a Reply

Your email address will not be published. Required fields are marked *