The simplest explanation

Imagine you log in to an online service and your profile page is at this URL:

https://example.com/profile/1042

The number 1042 is your user ID. What happens if you change it to 1041? If the application checks that you are only allowed to see profile 1042 — your own — you get an error or a redirect. But if the application skips that check and simply returns whatever profile matches the number you requested, you have just read another user's private data. That is IDOR.

The "direct object reference" part of the name refers to the fact that the URL or request directly references an internal object — in this case, a user account by its ID. The "insecure" part refers to the fact that the application does not properly check whether the person making the request has permission to access that object.

Why IDOR is so common

IDOR is one of those vulnerabilities that developers sometimes accidentally introduce precisely because their code works correctly — it retrieves the right record and displays it. The bug is not in the retrieval logic, it is in the missing authorisation check. The database query runs perfectly; the page renders correctly; it just happens to be showing the wrong person's data to the wrong person.

It is easy to miss during development because developers usually test their own features as themselves. A developer who builds a profile page tests it with their own account, and it works. They never test what happens when someone tries to access a different account's data, because that scenario does not come to mind naturally when you are building a feature rather than attacking it.

IDOR consistently appears in OWASP's Top 10 under the broader category of "Broken Access Control." It is considered so prevalent and so impactful that broken access control has been the number one web application security risk in recent years.

Real-world impact

IDOR vulnerabilities have exposed personal data, financial records, private messages, and medical information in breaches affecting millions of users. Because IDs are often sequential numbers, an attacker can iterate through thousands of records automatically.

IDOR in different parts of an application

IDOR does not only appear in profile URLs. It can exist anywhere in an application that uses an identifier to reference a resource. Here are some common examples:

URL parameters

The most visible form. A user can see and modify the ID directly in the browser address bar.

# An attacker simply increments the order ID to view other customers' orders:
https://shop.example.com/orders/14821
https://shop.example.com/orders/14822
https://shop.example.com/orders/14823

API endpoints

Modern web applications often have APIs that return JSON data. These endpoints are just as vulnerable to IDOR as traditional pages — and because they return structured data directly, they are often more efficient for an attacker to exploit at scale.

# A GET request to fetch a user's documents:
GET /api/documents/5551
# If there's no ownership check, changing the ID returns another user's document

Hidden form fields and POST requests

Not all object references are visible in the URL. Some are sent in the body of a POST request, in hidden form fields, or in request headers. These are not visible to a casual user, but anyone using browser developer tools or a proxy like Burp Suite can see and modify them.

File downloads

Applications that serve files using an ID or filename in the URL can be vulnerable to IDOR if they do not check that the requesting user owns the file being downloaded. Attackers can sometimes access other users' uploaded files, invoices, medical documents, or private reports.

What attackers can access through IDOR

The impact depends entirely on what the application stores and what data is accessible through the vulnerable endpoint. In practice, IDOR has been used to expose:

  • Full names, email addresses, phone numbers, and home addresses
  • Order history, payment information, and transaction records
  • Private messages and conversation histories
  • Medical records, test results, and appointment details
  • Tax documents, payslips, and financial statements
  • Admin-only data that regular users should never be able to see

In some cases, IDOR extends beyond reading data. If an application also allows objects to be modified or deleted via ID — and those operations also lack authorisation checks — an attacker can change or delete other users' data, take over their accounts, or cause significant damage.

How to prevent IDOR

Preventing IDOR requires one thing: checking that the currently authenticated user has permission to access the specific object they are requesting. This check needs to happen on the server for every request, every time.

Server-side ownership checks

Every time your application retrieves a resource by ID, the query or logic should include a condition that ensures the resource belongs to the authenticated user. It is not enough to check that the user is logged in — you need to verify that they own or are authorised to access the specific record they are requesting.

// VULNERABLE: fetches any order by ID with no ownership check
$order = Order::find($orderId);

// SAFE: only fetches the order if it belongs to the current user
$order = Order::where('id', $orderId)->where('user_id', auth()->id())->firstOrFail();

This single additional condition — where('user_id', auth()->id()) — means an attacker with a different user ID can never retrieve your records, even if they know the ID.

Use non-sequential, unpredictable identifiers

Sequential integer IDs (1, 2, 3...) make IDOR exploitation trivially easy — an attacker just has to iterate through numbers. Using UUIDs (universally unique identifiers) as your primary keys makes guessing or iterating over IDs practically impossible. A UUID looks like 550e8400-e29b-41d4-a716-446655440000 — there is no practical way to iterate through these.

However, using UUIDs is not a substitute for proper authorisation checks. It reduces the risk of accidental discovery, but a determined attacker who obtains a valid UUID through another means can still exploit a missing ownership check. Always do both.

Apply access control at every layer

Authorisation checks need to happen in your server-side code, not just in the UI. Hiding a button from users does not prevent them from making the same API request directly. Every endpoint that accesses data by ID should verify ownership or permission, regardless of whether the UI exposes that endpoint to all users or only some.

Test your application for IDOR

ScanexAI checks your web application's pages and API endpoints for IDOR patterns by analysing parameter structures and response behaviour. Run a free scan to detect broken access control on your site.

Summary

IDOR is a vulnerability that occurs when an application exposes a reference to an internal object — like a user ID, order ID, or file ID — and fails to verify that the requesting user has permission to access that specific object. An attacker can enumerate other objects simply by modifying the ID in a request.

Prevention requires server-side ownership checks on every endpoint that retrieves or modifies data by ID. Using non-sequential IDs like UUIDs adds an additional layer of difficulty. Access control logic must live in the server code — never rely on hiding options in the UI to protect sensitive data.