All Blogs
IDOR in Modern APIs: How Automated Pentesting Finds and Proves Exploitable Access

Quick Overview: IDOR is trivial to describe and brutal to test at scale. This article covers why object-level authorization breaks across APIs, GraphQL, microservices, and multi-tenant systems, what an automated IDOR detection pipeline has to do, and what evidence turns a suspicious response into a proven finding.
An attacker exploiting an IDOR does not need a payload, an encoder, or a memory corruption primitive. They need a valid session and a wrong identifier. They log in as themselves, watch the application request /api/invoices/4417, change one digit, and read someone else's invoice. Nothing was injected. Nothing was bypassed. The application did exactly what it was told. This is why API pentesting must validate object-level authorization across different user identities, rather than simply flagging requests that contain identifiers.
That is what makes Insecure Direct Object Reference so persistent. Every other vulnerability class has a signature that a scanner can pattern. IDOR has no signature. The malicious request is byte-for-byte identical in structure to the legitimate one, and the only difference between an attack and normal traffic is whether the requesting identity happens to own the object being requested. A tool cannot answer that question without knowing who owns what.
Modern architecture has made this worse in a specific way. Authorization used to live in one place. It now lives in a gateway, in a dozen services, in a GraphQL resolver, in a background export job, and in whatever code path a team shipped last sprint. Every one of those places is an independent opportunity to forget the ownership check. The scale of API security risk is reflected in Akamai's API security research, which found that 87% of surveyed organizations experienced an API-related security incident in 2025.
Detecting IDOR vulnerabilities is only half the problem. A tool that flags every user-controlled identifier as a potential IDOR produces a queue nobody works. The harder requirement is validation: proving that a substituted reference actually returned another principal's data, and doing it without destroying anything. And that's what we are going to about in this entire article.
Multi-identity testing and validated exploit evidence. No configuration required. Start Free Scan
On This Page
- What is IDOR, and Why Do APIs Call It BOLA?
- How IDOR Manifests in Modern Architectures?
- What Makes IDOR Hard to Automate?
- An Automated IDOR Detection Pipeline
- Validating an IDOR Finding: From Signal to Proof
- Why Traditional DAST Misses IDOR?
- Closing the Object-Level Authorization Gap
What is IDOR, and Why Do APIs Call It BOLA?
Insecure Direct Object Reference (IDOR) is an access control flaw in which an application accepts an object identifier supplied by the client and then reads, modifies, or deletes that object without verifying that the authenticated requester is entitled to it. The identifier can appear anywhere the client controls: a path segment, a query parameter, a JSON body field, a GraphQL variable, or a header.

The OWASP API Security Top 10 calls this same flaw Broken Object Level Authorization, and ranks it API1, its highest position. The two names describe the same object-level authorization vulnerability from different eras of the field. For a deeper treatment of the failure pattern itself, see our guide to broken object level authorization. This piece assumes that background and focuses on testing.
One correction is worth making early, because it drives a lot of false confidence: switching from sequential integers to UUIDs raises the cost of enumeration but is not an authorization control. A UUID that leaks through a share link, an export file, a referrer header, a support ticket, or a mobile app cache is just as usable as the integer it replaced. The server still has to check ownership.
How IDOR Manifests in Modern Architectures?
IDOR vulnerabilities in modern applications are caused by authorization logic that has been distributed across many services and re-implemented per endpoint, so that a single missed ownership check in any one of them exposes objects that every other layer protects correctly. The surfaces below are where that inconsistency shows up most often.
REST APIs and Request Bodies
Path parameters are the obvious case and usually the best-tested one. The gap tends to be everywhere else. An API IDOR vulnerability frequently lives in a PATCH body where the identifier is nested three levels deep, in a filter parameter that accepts an arbitrary account_id, or in a bulk endpoint that takes an array of identifiers and validates only the first. Teams that carefully guard GET /orders/{id} often leave POST /orders/bulk-export completely open, because it did not look like an object lookup when it was written.
GraphQL Resolvers and Nested Objects
GraphQL moves authorization to the resolver, and every resolver is an independent decision point. A top-level query may check ownership correctly while a nested field on the same type does not. Because a client can traverse relationships in a single request, an attacker can often reach an unprotected resolver through a protected one: request your own order, then walk to the customer object attached to it, then to that customer's other orders. Mutations carry the same risk with the added consequence of state change. Automated API security testing has to traverse the schema, not just hit the documented entry points.
Microservices and Service-to-Service Trust
A gateway validates the token and forwards the request inward. From there, downstream services frequently trust whatever context the caller supplied, on the reasoning that internal traffic is already authenticated. That reasoning conflates authentication with authorization. The gateway proved who the caller is; it did not prove the caller owns object 4418. When ownership logic is re-implemented in seven services by seven teams, the weakest implementation defines the security of the system.
Multi-Tenant Boundaries
In multi-tenant SaaS, the tenant boundary is the real trust boundary, and it is almost always enforced in application code rather than by the framework or the database. Every query has to be scoped by a tenant, on every path, forever. A single query that filters by object ID without also filtering by tenant ID produces cross-tenant access, which is the highest-severity form this flaw takes: it exposes a different customer's data, not merely a different user’s.
Object References Outside the Request Path
The channels that leak identifiers are rarely the ones under test. Presigned object storage URLs outlive the session that generated them. Report and export jobs return artifacts through a separate download route with its own authorization logic, or none. Webhook payloads deliver internal identifiers to third parties. Notification emails embed deep links. Each of these hands an attacker a valid reference through a path a scanner is not watching, which is why coverage of the full surface matters more than depth on any single endpoint.
Go beyond suspicious parameters with AI-powered testing that validates exploitable authorization failures. Validate IDOR Risks
What Makes IDOR Hard to Automate?
Object-level authorization testing is difficult to automate because a tool cannot judge a response as authorized or unauthorized without knowing which identity issued the request, which principal owns the object being requested, and what the application's genuine denial behavior looks like. Three problems have to be solved before any IDOR vulnerability testing can begin.
Identity Context and Session Durability
IDOR cannot be tested with one session. It requires at least two authenticated principals, and in practice a matrix: two users within a tenant, two tenants, and one account per privilege level. Establishing those sessions is an easy part of the process. Keeping them alive through a multi-hour scan is where most tooling fails. Access tokens expire mid-run. CSRF tokens rotate per request. Some applications invalidate all other sessions when one re-authenticates, so a naive refresh of session A silently kills session B and every subsequent comparison becomes meaningless. Robust authenticated security testing has to detect session death, re-establish independently, and discard results collected during the gap.
Object Ownership Mapping
Before a tool can ask whether user B can reach user A's object, it must know which objects belong to A. This is a crawl-and-attribute problem, not a payload problem. The tool navigates as A, records every identifier A legitimately receives, and tags each one with its owner, its type, and the endpoint that returned it. Objects created during the scan have to be attributed to creation time. Without this map, reference substitution is blind guessing, and a 200 response proves nothing because the tool has no idea whose object came back.
Distinguishing Authorized from Unauthorized Responses
Applications lie about authorization outcomes constantly, usually by accident. A denial can come up as 403, as 404 to avoid confirming the object exists, as 200 with an empty collection, as 200 with a generic error rendered in HTML, or as a 302 to a login page. Meanwhile, a successful cross-account read may return a 200 that looks structurally identical to the requester's own data. Status codes are not a signal. The tool needs response diffing against a known-authorized baseline, structural comparison rather than byte comparison, and a similarity threshold tuned per endpoint.
An Automated IDOR Detection Pipeline
Automated IDOR detection works by discovering every endpoint and object reference in the application, establishing multiple authenticated identities across roles and tenants, substituting references between those identities across all supported operations, and analyzing the responses against a known-authorized baseline to determine whether an unauthorized object was returned. The four stages below run in order, and each depends on the one before it.
1) Discovery and Object Reference Extraction
Coverage bounds everything downstream. An endpoint that is never discovered is never tested, which makes shadow and undocumented routes the most common source of missed findings. The crawl has to render JavaScript to reach SPA-driven calls, follow API traffic rather than only links, introspect GraphQL schemas where enabled, and ingest OpenAPI definitions where available. Extraction then identifies which parameters are object references rather than filters or flags, across paths, query strings, bodies, headers, and GraphQL variables.
2) Identity, Role, and Tenant Matrix
This is how to test object-level authorization in practice: build a matrix rather than a pair. At minimum, two peer users in one tenant to test horizontal access, one low-privilege and one high-privilege account to test vertical access, and one user in a second tenant to test isolation. Each session is maintained independently, with its own token lifecycle and its own liveness check. The matrix defines every comparison the scan is capable of making, so gaps here become blind spots later.
3) Reference Substitution Across Methods and Operations
Substitution replaces an identifier owned by A with one owned by B, then replays the request under B's session. The important discipline is breadth of operation rather than volume of requests. The same object identifier should be exercised across every method the endpoint accepts, because read access and write access are frequently guarded by different codes. An endpoint that correctly rejects GET /documents/{id} may happily accept DELETE /documents/{id}. GraphQL requires the same treatment across queries, mutations, and nested field traversal.
4) Authorization Decision Analysis
Each response is compared against two baselines: the owner's authorized response for that object, and a known-denied response from the same endpoint. A finding requires the substituted response to resemble the former and diverge from the latter. Structural similarity, presence of owner-specific fields, response size, and timing all contribute. This stage produces a candidate, not a conclusion. Everything it emits still has to be proven.
See what automated pentesting costs before a broken authorization flaw becomes a breach.See Pentesting Pricing
Validating an IDOR Finding: From Signal to Proof
To validate an IDOR vulnerability, confirm that the object returned to the unauthorized session is the specific object owned by another principal, by matching a unique field value recorded from the owner's own authorized response against the data returned to the attacker session. Detection produces a candidate. Validation is what makes it a finding a developer will accept without argument.
An IDOR vulnerability is exploitable when a request carrying one principal's credentials returns or alters another principal's object, and the returned data is verifiably that object rather than a coincidentally similar record, an empty shell, or a generic response. That distinction is the entire difference between a real finding and the noise that makes teams stop reading scanner output.

Validating State-Changing IDOR Safely
Read-path IDOR is safe to prove. Write and delete paths are not, and this is where automated IDOR exploit validation has to be deliberately conservative. Deleting a real object to demonstrate a vulnerability is not an acceptable outcome in a production environment.
The workable sequence is to confirm the read path first, then attempt write operations only against objects the testing identities created during the scan, so that any successful modification destroys nothing of value. Where no scan-created object exists on a given endpoint, exploitability is inferred from the read result and the endpoint's method support rather than demonstrated, and the finding is reported with that limitation stated explicitly. Automated penetration testing running against live environments has to make this distinction visible rather than quietly attempting the destructive path.
What the Evidence Package Has to Contain
A finding that a developer can act on without re-testing it themselves needs the full request pair with headers, the identity used for each request and how it was obtained, the specific parameter that was substituted, the unique field value that proves object identity, and the denial baseline showing what a correct rejection looks like on that endpoint. Access control validation that stops short of the unique field value is asking the reader to take the result on trust, which is how findings end up disputed and then ignored.
Why Traditional DAST Misses IDOR?
Legacy vulnerability scanners or DAST tools fail on IDOR because they were built around a single authenticated session and a payload library. Manual pentesting solves both problems correctly, which is why it still finds the IDORs that tooling misses, but it covers a sample of endpoints and its results go stale on the next deploy. Neither model produces continuous, evidence-backed access control validation.
| Capability | Legacy DAST | Manual Pentest | Application-Aware Automated Pentesting |
|---|---|---|---|
| Multiple identities | Single session | Yes, manually configured | Role and tenant matrix, maintained through the scan |
| Object ownership map | None | Held by the tester | Built during crawl, attributed per identity |
| Exploit validation | Status code heuristics | Manual confirmation | Unique-field matching against owner baseline |
| Coverage | Documented routes | Sampled by time budget | Crawled, SPA-rendered, schema-aware |
| Re-test cadence | Scheduled scan | Quarterly or annual | Every build, via CI/CD |
| False positive rate | High on access control | Low | Near zero, evidence-gated |
The gap is not scanning speed. It is that dynamic application security testing built on a single identity has no basis for judging an authorization decision, and adding more payloads does not fix that.
Catch IDOR vulnerabilities before attackers exploit broken object-level authorization across your APIs. Start Testing
Closing the Object-Level Authorization Gap
IDOR persists because it is invisible to the tools most teams point at it. There is no anomalous input to flag and no signature to match, only a question about ownership that a single-session scanner has no way to answer. As applications split into APIs, resolvers, services, and tenant-scoped queries, the number of independent places that question gets asked keeps growing, while the number of places it gets asked correctly does not keep pace.
ZeroThreat approaches this as an identity and evidence problem. Its engine maintains multiple authenticated principals across roles and tenants for the duration of a scan, attributes discovered objects to their owners during the crawl, substitutes references across REST methods and GraphQL operations, and gates every finding on proof that a specific victim-owned object was returned to a session that had no right to it. Findings arrive with the request pair, the identity used, the substituted parameter, and the unique field that proves it, which is what makes them actionable rather than arguable. This is what effective API pentesting should deliver: evidence-backed validation of broken object-level authorization across identities, roles, and tenants.
If broken object-level authorization is a risk you are carrying without measuring, the fastest way to find out is to test for it. Start a free scan and see what your object references actually expose.
Frequently Asked Questions
What is the difference between IDOR and BOLA?
IDOR is the older term from the web application era, and Broken Object Level Authorization is the name OWASP adopted for its API Security Top 10, where it is ranked API1. BOLA is the more precise label because it names the missing control, which is the object-level authorization check, rather than the symptom of an exposed reference. Both describe an application returning or modifying an object based on a client-supplied identifier without verifying ownership.
How does object-level authorization prevent IDOR?
Can IDOR exist in applications that use an API gateway?
Explore ZeroThreat
Automate security testing, save time, and avoid the pitfalls of manual work with ZeroThreat.


