logo svg
logo

November 4, 2026

Updated: November 4, 2026

IDOR Vulnerability: Examples, BOLA and How to Prevent It

Definition, IDOR vs BOLA, the OWASP and CWE mapping, real CVE cases, multi-account testing and prevention.

Mohammed Khalil

Mohammed Khalil

Featured Image

An insecure direct object reference (IDOR) is an access control vulnerability in which an application uses an identifier supplied by the user, such as an account number, file name or record ID, to fetch or change an object without checking that the requester is allowed to access that specific object.

In APIs the same flaw is called broken object level authorization (BOLA), ranked API1:2023 by OWASP, and it falls under A01 Broken Access Control in the OWASP Top 10. The fix is a server-side ownership check on every request, not hidden or random IDs.

What Is an IDOR Vulnerability?

Every web application and API refers to things: invoices, messages, files, orders, user profiles, tenants. To fetch one, the client sends a reference, usually an ID in the URL, the request body, a header or a cookie. An IDOR exists when the server trusts that reference and returns or changes the object without asking one question: does this user have the right to this particular object?

The OWASP IDOR Prevention Cheat Sheet describes three ingredients:

  1. An object, such as an account, document, support ticket, transaction or partner profile.
  2. A reference to that object, such as an ID, UUID, account number, token or slug.
  3. A missing object-level authorization check that lets a user read or change an object they should not reach.

Remove the third ingredient and the first two are harmless. That is the most important point in this guide: IDOR is not caused by exposing IDs. It is caused by not checking them.

MITRE classifies the weakness as CWE-639: Authorization Bypass Through User-Controlled Key, lists both IDOR and BOLA as alternate terms, and places it under CWE-863 (Incorrect Authorization). The ACSC, CISA and NSA joint advisory AA23-208A, published July 27, 2023, warned that IDOR flaws are "frequently exploited by malicious actors in data breach incidents" because they are common, hard to prevent outside the development process and can be abused at scale.

Authentication is not authorization

Most IDORs sit behind a login. The application correctly confirms who the user is (authentication) but never confirms what that user may touch (authorization). A valid session cookie proves the request came from Alice. It says nothing about whether invoice 1002 belongs to Alice. Frameworks handle authentication well out of the box; object-level authorization depends on business rules that only your code knows, which is why it is so often missing.

Types of IDOR

The joint advisory groups IDOR into four types. They overlap, and one endpoint can show more than one.

TypeWhat the attacker gainsExample (illustrative)
HorizontalAccess to data of another user at the same privilege levelA customer reads another customer's order history
VerticalAccess to data that requires a higher privilege levelA standard user reads an admin-only audit record by its ID
Object-levelAbility to modify or delete an object they should not changeA user deletes another user's uploaded file
Function-levelAccess to a function or action they should not useA user triggers an approval action reserved for managers

Horizontal IDOR is the classic case. Vertical and function-level IDOR shade into privilege escalation and broken function level authorization, covered below.

How an IDOR Happens

The examples below are illustrative pseudo-requests against example.com, a domain reserved for documentation. They show the logic, not an attack on any real system.

1. An ID in the URL path. Alice is logged in and opens her invoice:

text
GET /api/invoices/1001 HTTP/1.1
Host: example.com
Cookie: session=<alice-session>

The server responds with invoice 1001. Alice changes one digit:

text
GET /api/invoices/1002 HTTP/1.1
Host: example.com
Cookie: session=<alice-session>

If the server returns Bob's invoice with a 200 status, the endpoint has an IDOR. The server checked that the session was valid and that invoice 1002 existed. It never checked that invoice 1002 belonged to Alice.

2. An ID in the request body. A profile update form sends the user ID in a JSON body:

text
PATCH /api/profile HTTP/1.1
Host: example.com
Cookie: session=<alice-session>
Content-Type: application/json

{"user_id": 4471, "email": "alice@example.com"}

If the server updates whatever user_id the client sends instead of the user tied to the session, Alice could change another user's email address, which may lead to account takeover through password reset. The safer design does not accept user_id from the client at all for this action; it takes identity from the session.

3. A reference that is not a number. IDOR is not limited to sequential integers. File names, order codes, email addresses, Base64-encoded values and UUIDs are all object references:

text
GET /exports/2026-q3-report-acme.pdf HTTP/1.1
Host: example.com

If another tenant's report is reachable by changing the file name, it is the same flaw.

The vulnerable server logic in all three cases looks like this, in pseudo-code:

text
// Vulnerable: looks up by ID only
invoice = db.invoices.find(id = request.path.id)
return invoice

// Fixed: scopes the lookup to the authenticated user
invoice = db.invoices.find(id = request.path.id, owner_id = session.user_id)
if invoice is null: return 404
return invoice

Where object references hide

LocationTypical exampleOften missed because
URL path/api/users/123/settingsDevelopers assume the UI only links to the user's own records
Query string?account=88231Parameters added late for convenience
Request body (JSON or form)"order_id": 5520 or a hidden form fieldHidden fields feel trusted
Headers and cookiesX-Account-Id or an account cookieTreated as infrastructure, not input
GraphQL argumentsnode(id: "…") or a mutation argumentOne resolver checks ownership and another does not
Nested resources/orders/77/items/3Parent checked, child not
File and export names/downloads/invoice-2026-0042.pdfStatic file serving bypasses app logic
Asynchronous jobs/reports/status?job=af31…Job IDs assumed to be secret
Shared or batch endpoints/api/bulk?ids=1,2,3Each ID in the batch is not checked
IDOR request flow: an authenticated user changes an invoice ID and gets another user's data until an ownership check is added

IDOR vs BOLA: The OWASP Mapping

IDOR and BOLA describe the same root cause from two communities. Web application security has used "IDOR" since the 2007 OWASP Top 10, which, as PortSwigger's Web Security Academy notes, popularized the term. API security uses "BOLA," the name the OWASP API Security Top 10 gave the risk in 2019 and kept at number one in 2023.

The OWASP API1:2023 Broken Object Level Authorization entry rates exploitability as easy, prevalence as widespread and detectability as easy, and describes impact ranging from data disclosure to data manipulation and, in some cases, full account takeover. Its scenarios include an API that accepts a vehicle identification number without checking ownership, letting an attacker control other people's cars, and a GraphQL mutation that deletes any document by ID.

Practitioners use the terms interchangeably, but there are nuances. MITRE notes that IDOR is in some ways broader, because it has also been used for path traversal, and the CWE team suspects IDOR is part of BOLA. In practice: if a user can act on an object they do not own by changing its identifier, call it IDOR in a web report and BOLA in an API report, and tag it CWE-639.

TermSourceWhat failsExample (illustrative)
IDOROWASP Top 10 (2007–2013 A4), common web termObject access by a user-controlled reference without an ownership checkGET /invoices/1002 returns another user's invoice
BOLAOWASP API Security Top 10, API1:2019 and API1:2023Same as IDOR, in an API endpointAn API returns any shop's revenue data when the shop name changes
BOPLAOWASP API3:2023Authorization on individual properties of an object (mass assignment or excessive data exposure)A user sets "role": "admin" in their own profile update
BFLAOWASP API5:2023Access to a whole function or endpoint the user should not reachA standard user calls DELETE /admin/users/9
Broken Access ControlOWASP Top 10, A5:2017, A01:2021, A01:2025The umbrella category for all of the aboveAny of the rows above

The line between BOLA and BFLA matters in reports. OWASP's BOLA entry explains that if the user may call the endpoint but changes the object ID, it is BOLA; if the user should not reach the endpoint at all, it is broken function level authorization. Broken object property level authorization is about fields inside an object you are allowed to reach.

Where IDOR sits in the OWASP Top 10

IDOR has its own history in the web Top 10:

So when someone asks whether IDOR is still in the OWASP Top 10, the answer is yes: it is one of the named patterns inside the number one category, and the number one API risk on its own.

For how IDOR compares with the other high-impact web flaws we see, read our list of the most common web vulnerabilities.

How IDOR maps to OWASP Top 10 Broken Access Control, OWASP API1 BOLA and CWE-639 across editions

IDOR vs privilege escalation

Privilege escalation is the outcome; IDOR is one way to get there. A horizontal IDOR gives a user the reach of a peer (horizontal privilege escalation). A vertical or function-level IDOR gives a user the reach of a more privileged role (vertical privilege escalation). Privilege escalation can also come from other flaws, such as a misconfigured role, a forged token or a missing function-level check, so not every privilege escalation is an IDOR. For both directions of escalation beyond web apps, including Windows, Linux and cloud IAM causes, see our guide to privilege escalation.

Real IDOR Examples: Publicly Disclosed Cases

The cases below are public, dated and traced to primary sources: a CVE record, a vendor advisory, a CERT/CC note, the CISA Known Exploited Vulnerabilities (KEV) catalog or a regulator's order. Each one teaches a different lesson.

DateCaseWhat happenedIdentifier typeLesson
2012Mobile carrier subscriber dataThe joint advisory cites a 2012 incident in which an actor obtained personal data of more than 100,000 mobile device owners from a US communications company's public websiteDevice identifierIDOR has been exploited at scale for more than a decade
May 2019First American Financial (SEC order June 15, 2021)A document-sharing application exposed over 800 million document images dating back to 2003; the SEC found security staff had identified the vulnerability months earlier but had not remediated itSequential document number in a linkPredictable IDs plus no access check equals mass exposure; unremediated findings become regulatory problems
Oct 2021 to Feb 2022Stalkerware back end, CVE-2022-0732Shared infrastructure behind multiple phone-monitoring apps did not authenticate or authorize API requests, exposing data from hundreds of thousands of devicesAPI object identifierShared back ends multiply the blast radius of one missing check
Jan 2023 (CVE published)Instructure Canvas LMS, CVE-2021-36539The file preview URL did not deny access to locked or unpublished files for unprivileged usersFile identifier in a preview URLSecondary views (previews, exports) need the same check as the main view
Added to KEV Jan 26, 2023Telerik UI for ASP.NET AJAX, CVE-2017-11357CISA and the vendor describe an insecure direct object reference in RadAsyncUpload that can result in file uploads and, in some cases, remote code execution; KEV marks known ransomware campaign useUpload handling referenceIDOR is not only a data-read bug; impact depends on what the object controls
Jun 2026 (advisory), KEV Jul 7, 2026Langflow, CVE-2026-55255An authenticated user could execute any flow belonging to another user by specifying the victim's flow ID; the lookup by UUID skipped the ownership check; fixed in Langflow 1.9.1UUIDRandom identifiers did not prevent exploitation; only the ownership check fixed it

Sources for the table: the ACSC, CISA and NSA advisory AA23-208A (2012 and 2019 cases); the SEC press release of June 15, 2021 and the original KrebsOnSecurity report of May 24, 2019; CERT/CC VU#229438 and NVD CVE-2022-0732; NVD CVE-2021-36539; NVD CVE-2017-11357; the Langflow security advisory GHSA-qrpv-q767-xqq2 and NVD CVE-2026-55255; and the CISA KEV catalog.

What the cases have in common

First American shows the cost of predictable identifiers. According to the KrebsOnSecurity report the SEC order followed, anyone with a valid document link could view other people's records by changing a single digit, and document numbers appeared to be issued sequentially. The SEC order focused on disclosure controls: senior executives were not told that information security staff had found the vulnerability several months earlier. First American agreed to pay a $487,616 penalty without admitting or denying the findings. An IDOR finding that sits unfixed is a governance issue, not only a technical one.

The stalkerware case shows how far one flaw can reach. CERT/CC reported that an unauthenticated remote attacker could access personal information collected from any device running one of the affected app variants, and that it knew of no practical solution because the infrastructure provider had to fix it.

Langflow shows why UUIDs are not a fix. Langflow's flow IDs are UUIDs, which are hard to guess. The flaw still existed because the helper that loaded a flow by UUID did not filter by the requesting user, while the alternate lookup path did. Once attackers can learn an ID, from logs, shared links, other API responses or a colleague, random IDs offer no protection. CISA added the CVE to the KEV catalog on July 7, 2026, which means it had evidence of exploitation in the wild.

Telerik shows IDOR impact is not limited to reading data. When the object reference controls where a file goes or what a component does, the impact can reach code execution. Severity depends on the object, not on the bug class name.

How Testers Find IDOR: Multi-Account and Role-Matrix Testing

IDOR testing is authorization testing, and authorization only makes sense relative to who owns what. That is why the core technique uses at least two accounts. The OWASP Web Security Testing Guide covers it as WSTG-ATHZ-04, Testing for Insecure Direct Object References, and the joint advisory recommends mapping every location where user input references objects and testing with users of different privilege levels.

Run these steps only in an environment you own or are authorized to test, with test accounts and test data.

  1. Create the accounts. At minimum, two users in the same role (A and B). For multi-tenant applications, add a user in a second tenant (C). Add one account per role that matters, such as read-only, manager and admin, and keep an unauthenticated session for comparison.
  2. Create objects for each account. Each account owns its own invoices, files, tickets or projects, so you know the ground truth: which object belongs to whom.
  3. Map every object reference. Browse the application and read the API documentation (OpenAPI, GraphQL schema, Postman collections) while capturing traffic in a test proxy. Note every ID in paths, query strings, bodies, headers, cookies, GraphQL arguments and file names, plus IDs that appear in responses.
  4. Build the role matrix. For each object type, list each action (read, create, update, delete, export, share, approve) against each account and record the expected result: allow or deny.
  5. Replay requests across accounts. Take a request made by A on A's object and send it with B's session, then C's, then the read-only session, then no session. Compare the responses with the expected matrix.
  6. Vary the method and path. If GET is protected, check PUT, PATCH and DELETE on the same object. Check older API versions and alternative routes to the same object, nested child objects, batch endpoints and export or preview features.
  7. Confirm impact safely. A deny cell that returns data or changes state is a finding. Confirm with your own test objects only, record the request and response pair as evidence, and stop there.
Role matrix for IDOR testing showing which accounts should be allowed or denied read, update, delete and export on one object

Testers also look for the places where the matrix is most likely to break: features added recently, endpoints used only by mobile apps, admin and support tooling, file and report exports, invitations and sharing links, and any flow where one object references another (an order that references an address, a ticket that references an attachment). For GraphQL-specific patterns such as per-resolver authorization, see our guide to GraphQL API vulnerabilities.

A good finding write-up states the account used, the object owner, the exact request and response, the expected matrix cell and the business impact. Our penetration testing report guide shows how that evidence should be structured so developers can reproduce and fix it.

Turning the matrix into automated tests

Once a tester has built a role matrix, keep it. The OWASP Authorization Testing Automation Cheat Sheet describes how to formalize the expected authorization matrix and run integration tests against it. In practice, that means a CI test for each sensitive endpoint that logs in as user B and asserts that user A's object returns 403 or 404. OWASP's BOLA guidance says the same: write tests for the authorization mechanism and do not deploy changes that make them fail.

Why Scanners Miss IDOR

Automated scanners are useful for many vulnerability classes, but IDOR is hard for them for a structural reason: the request that exploits an IDOR is a perfectly valid request. Nothing in the syntax is malicious. Only knowledge of ownership tells you the 200 response is wrong.

OWASP put it plainly in its 2017 Broken Access Control entry: SAST and DAST tools "can detect the absence of access control but cannot verify if it is functional." The joint advisory adds that not all DAST tools can detect IDOR, and those that can may need specific environment setup and custom rules.

Scanner limitationWhy it matters for IDORWhat a human tester adds
Usually runs with one sessionIDOR needs a second identity to prove cross-user accessTwo or more accounts per role and tenant
Does not know who owns which objectA 200 response looks like success, not a breachGround truth from objects the tester created
Treats IDs as opaque stringsCannot judge whether a returned record is someone else's dataReads the response and recognizes another user's data
Weak on multi-step and nested flowsMany IDORs sit in exports, shares, approvals and child objectsFollows business workflows end to end
Limited GraphQL and asynchronous coverageResolver-level and job-ID checks are missedTests each resolver and job reference
SAST sees code, not policyCannot know the business rule "managers see their team's records only"Compares behavior against the intended policy

Tools still help. Proxy extensions that replay requests with a second session, API scanners configured with multiple users, and schema-driven test generators can cover the repetitive part of the matrix. Our roundup of API security testing tools compares options. The judgment about whether a response violates policy, and the creativity to find the reference nobody listed, still come from people. That is also why bug bounty programs report so many access control findings; our comparison of penetration testing vs bug bounty explains when each fits.

How to Prevent IDOR

Prevention comes down to one rule from OWASP A01:2025: access control is only effective in trusted server-side code, where the attacker cannot modify the check. Everything else is defense in depth.

ControlStrengthWhat it doesNotes
Server-side object-level authorization on every requestRequiredChecks that the requester may perform this action on this objectCovers read, update, delete, export and every HTTP method
Scope data access to the user or tenantRequiredQueries only records the user can reach, so other records are never loadedFor example, look up the invoice through the current user's invoices
Deny by defaultRequiredAny object or function without an explicit rule is refusedOWASP A01 lists it first
Centralized authorization policyStrongly recommendedOne middleware, policy engine or data-layer filter enforces rules everywherePrevents the "one endpoint forgot" problem
Identity from the session, not the clientStrongly recommendedThe server ignores user_id or tenant_id values sent by the client for the caller's own actionsRemoves a whole class of body-parameter IDOR
Indirect referencesDefense in depthMaps a per-user or per-session reference (1, 2, 3 in "your list") to the real ID on the serverExposes no global IDs; still check authorization
Random, non-sequential IDs (UUIDs)Defense in depthMakes enumeration much harderNever a substitute for authorization, as Langflow showed
Authorization tests in CIStrongly recommendedTwo-user tests fail the build when a check disappearsTurn every IDOR finding into a regression test
Logging, alerting and rate limitsDetectiveSpots enumeration patterns, such as bursts of 403 and 404 responsesReduces scale; does not fix the flaw

Server-side authorization checks

Put the check where the object is loaded, not only at the route. The OWASP cheat sheet's example contrasts looking up a project from all projects with looking it up from the current user's projects. The same idea in language-neutral pseudo-code:

text
// Avoid: global lookup, then hope a later check runs
project = Projects.find(request.id)

// Prefer: lookup scoped to what the caller may access
project = Projects.where(tenant_id = session.tenant_id)
                  .where(member = session.user_id)
                  .find(request.id)
if project is null: return 404   // do not reveal whether it exists

// For actions beyond read, check the action too
if not policy.allows(session.user, "delete", project): return 403

Three implementation details matter:

The OWASP Authorization Cheat Sheet covers broader design choices, such as enforcing least privilege, validating permissions on every request and preferring attribute- or relationship-based models where roles alone are too coarse.

Indirect references

An indirect reference map replaces the real database key with a value that only means something for the current user or session. A user who sees "document 3" in their list sends 3; the server translates it to the real ID from that user's own list, so a reference cannot point outside it. The joint advisory recommends indirect reference maps and replacing exposed IDs with cryptographically strong random values such as UUIDs, while noting that UUIDs should not be used as a security capability.

Use indirect references and random IDs to make enumeration harder, and keep the object-level check anyway. The Langflow case is the reminder: the IDs were UUIDs and the flaw was still exploitable.

Build it into the lifecycle

IDOR is a design and code-review problem first. Add an authorization model to the threat model for every feature that introduces a new object type, review access checks during code review, and keep the role matrix as a living test artifact. Our secure SDLC guide shows where these checks belong in each phase. For context on how often API authorization issues appear in industry data, see our API security statistics.

IDOR Testing Checklist

Use this checklist when you scope or review an authorization test:

Frequently Asked Questions

What is IDOR in simple terms?

IDOR (insecure direct object reference) is a flaw where an application lets you reach someone else's data or actions just by changing an ID in a request, because the server does not check whether that object belongs to you. It is a type of broken access control.

What is the difference between IDOR and BOLA?

They describe the same root cause: a missing object-level authorization check on a user-controlled identifier. IDOR is the traditional web application term from the OWASP Top 10; BOLA is the API term, ranked API1 in the OWASP API Security Top 10 in 2019 and 2023. MITRE lists both as alternate terms for CWE-639.

What is the difference between IDOR and privilege escalation?

Privilege escalation is the result: gaining access beyond what your account should have. IDOR is one cause. A horizontal IDOR gives you another user's data at the same level; a vertical or function-level IDOR gives you access reserved for a higher role. Privilege escalation can also come from other flaws, such as role misconfiguration or token forgery.

Is IDOR still in the OWASP Top 10?

Yes. IDOR was its own category (A4) in 2007, 2010 and 2013, was merged into A5:2017 Broken Access Control, and is named as a common case inside A01:2025 Broken Access Control, the top-ranked category. In the OWASP API Security Top 10, the same flaw (BOLA) is API1:2023.

Do UUIDs prevent IDOR?

No. Random identifiers make IDs harder to guess, which reduces enumeration, but IDs leak through shared links, logs, emails and other API responses. Langflow's CVE-2026-55255 involved UUID-based flow IDs and was still exploitable. Only a server-side ownership check prevents IDOR.

Can automated scanners find IDOR?

Sometimes, with effort. Scanners usually run with one session and cannot tell whether a successful response contains another user's data. Some DAST and API tools can test with multiple users if configured with custom rules, but OWASP and the joint ACSC, CISA and NSA advisory both say manual, multi-account testing is still needed.

What CWE is IDOR?

CWE-639, Authorization Bypass Through User-Controlled Key. It is a child of CWE-863 (Incorrect Authorization) and the parent of CWE-566 (Authorization Bypass Through User-Controlled SQL Primary Key). CVE records for IDOR often also cite CWE-284 or CWE-285.

How severe is an IDOR vulnerability?

It depends on the object. Reading a public display name is low impact; reading financial records, changing another user's email, deleting files or triggering actions can be high or critical. OWASP rates BOLA exploitability as easy, and the cases above range from mass data exposure to remote code execution.

How do you fix an IDOR vulnerability?

Add a server-side authorization check where the object is loaded, scope queries to the current user or tenant, deny by default, take the caller's identity from the session instead of the request, and add a two-user regression test. Random IDs and indirect references are useful extra layers, not the fix.

Next Step: Test Object-Level Authorization Like an Attacker

IDOR is simple to explain and easy to miss, because every exploiting request looks legitimate. The defense is equally simple to state: check ownership on the server for every object, every action and every path, and prove it with tests that use more than one account.

DeepStrike's web application penetration testing is built for this class of flaw: rigorous manual testing based on how your application is supposed to behave, security checks from multiple users' perspectives, findings validated through exploitation, fix recommendations, a technical presentation for your engineers and re-testing to confirm the fixes hold. If your exposure is mostly APIs, our API penetration testing guide explains how BOLA, BFLA and tenant isolation testing are scoped. To discuss your application, contact DeepStrike.

About The Author

Mohammed Khalil is a Cybersecurity Architect at DeepStrike, specializing in advanced penetration testing and offensive security operations. With certifications including CISSP, OSCP, and OSWE, he has led numerous red team engagements for Fortune 500 companies, focusing on cloud security, application vulnerabilities, and adversary emulation. His work involves dissecting complex attack chains and developing resilient defense strategies for clients in the finance, healthcare, and technology sectors.

Sources and References

background
Let's hack you before real hackers do

Stay secure with DeepStrike penetration testing services. Reach out for a quote or customized technical proposal today

Contact Us