August 1, 2026
How IDOR/BOLA Becomes a High-Impact API Vulnerability
Triage teams close it as Low. The attacker reopens it as Critical. Hereβs the gap.

By CYBER MIND SPACE
7 min read
Authorization flaws like IDOR have seen a massive spike in disclosures and are behind some of the largest data breaches of the past two years. Yet when a researcher submits an IDOR finding, the first response from triage is often: "Limited impact. Marking P3."
Both statements are true β and that tension is exactly the problem.
IDOR is not inherently high impact. It becomes high impact through a set of escalation factors that most triage workflows never look for, and most developers never test against. Understanding those factors β precisely and technically β is the difference between a $150 acknowledgment and a $15,000 payout.
π What IDOR Actually Is (And What It Isn't)
IDOR is an access control vulnerability that occurs when an application exposes internal object references β such as database keys, file names, or record IDs β and fails to verify that the requesting user is authorized to access the referenced object.
The critical word is authorization. Not authentication.
AUTHENTICATION vs AUTHORIZATION
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Authentication: "Who are you?"
β Token validated
β Session active
β User confirmed: user_id = 4821
Authorization: "What are you allowed to touch?"
β Never checked.
β user_id 4821 requests object owned by 4822.
β Server returns it anyway.
IDOR lives in this gap.AUTHENTICATION vs AUTHORIZATION
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Authentication: "Who are you?"
β Token validated
β Session active
β User confirmed: user_id = 4821
Authorization: "What are you allowed to touch?"
β Never checked.
β user_id 4821 requests object owned by 4822.
β Server returns it anyway.
IDOR lives in this gap.BOLA is a logic flaw rather than a typical coding error. The server correctly verifies the user's identity but fails to verify if the authenticated user has the rights to access the specific resource or object requested. That's why no scanner catches it β the request is structurally valid, the response is structurally valid, and there are no payloads to signature-match.
βοΈ The 5 Factors That Lift IDOR From Low to Critical
Most IDOR reports are graded on what the object is. High-impact IDOR reports are graded on what the object enables.
Factor 1 β Object sensitivity
Low impact objects: usernames, display names, public bios
Medium impact objects: emails, phone numbers, private messages
High impact objects: payment methods, API keys, session tokens
Critical impact objects: password reset tokens, 2FA secrets, admin credentialsLow impact objects: usernames, display names, public bios
Medium impact objects: emails, phone numbers, private messages
High impact objects: payment methods, API keys, session tokens
Critical impact objects: password reset tokens, 2FA secrets, admin credentialsThe same endpoint pattern (GET /api/resource/{id}) carries wildly different severity depending on what's stored at the referenced object. An IDOR that exposes a user's public profile photo is not the same finding as one that exposes their TOTP seed.
Factor 2 β HTTP method: Read vs Write vs Delete
READ β GET /api/orders/8821 Severity: LowβMedium
returns order details
WRITE β PATCH /api/orders/8821 Severity: MediumβHigh
modifies order status, address, payment method
DELETE β DELETE /api/orders/8821 Severity: High
irreversibly removes another user's data
WRITE on auth fields:
PATCH /api/users/4822 Severity: Critical
{ "email": "attacker@evil.com" }
β Redirects password reset to attacker βREAD β GET /api/orders/8821 Severity: LowβMedium
returns order details
WRITE β PATCH /api/orders/8821 Severity: MediumβHigh
modifies order status, address, payment method
DELETE β DELETE /api/orders/8821 Severity: High
irreversibly removes another user's data
WRITE on auth fields:
PATCH /api/users/4822 Severity: Critical
{ "email": "attacker@evil.com" }
β Redirects password reset to attacker βIDOR allows accessing any user's private documents, personal messages, and even admin panels. Bounties are typically $1,000β$5,000, depending on the data exposed. Write and delete IDOR consistently land at the top of that range. A write IDOR on an email field is often triaged as Critical β it converts a data read into a full account takeover.
Factor 3 β Scope (Single user vs mass exposure)
One IDOR that requires knowing a specific ID is a Medium. The same IDOR on a sequentially-indexed endpoint is a Critical β because it's scriptable across the entire user base. Demonstrating that the finding affects not one account but _N_accounts changes the reported impact calculation entirely.
Factor 4 β Cross-tenant access
In a multi-tenant SaaS product, an IDOR that crosses organizational boundaries carries automatic severity elevation β it's a data isolation failure, not just a missing ownership check. Regulators treat unauthorized access to personal data as a breach regardless of how trivial the technique was, so the compliance bill arrives the same as it would for any other exposure.
Factor 5 β Whether the object is an access credential itself
An IDOR that exposes an API key, a webhook secret, or an active session token isn't just a data leak β it's a pivot point. The leaked credential becomes the entry point for the next attack stage.
π The UUID Myth β Why "Random IDs" Don't Fix IDOR
This is the most common false sense of security in API development.
DEVELOPER'S BELIEF
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
"We switched from sequential integers to UUIDs.
Nobody can guess a550e8400-e29b-41d4-a716-446655440000.
IDOR is no longer possible."
ATTACKER'S REALITY
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
UUIDs leak constantly:
Source 1 β Email notifications
"Your invoice is ready: /invoice/a550e8400-..."
Source 2 β API list endpoints
GET /api/projects
Response: [ {"id": "a550e8400-...", "name": "..."} ]
β Every project UUID now in attacker's hands
Source 3 β JavaScript bundles
jsluice / LinkFinder β harvest hardcoded object refs
Source 4 β Referrer headers
User shares a link. Referrer exposes the UUID.
Source 5 β UUID v1 timestamp prediction
Version 1 UUIDs encode creation time + MAC address.
Predictable to millisecond-level precision.DEVELOPER'S BELIEF
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
"We switched from sequential integers to UUIDs.
Nobody can guess a550e8400-e29b-41d4-a716-446655440000.
IDOR is no longer possible."
ATTACKER'S REALITY
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
UUIDs leak constantly:
Source 1 β Email notifications
"Your invoice is ready: /invoice/a550e8400-..."
Source 2 β API list endpoints
GET /api/projects
Response: [ {"id": "a550e8400-...", "name": "..."} ]
β Every project UUID now in attacker's hands
Source 3 β JavaScript bundles
jsluice / LinkFinder β harvest hardcoded object refs
Source 4 β Referrer headers
User shares a link. Referrer exposes the UUID.
Source 5 β UUID v1 timestamp prediction
Version 1 UUIDs encode creation time + MAC address.
Predictable to millisecond-level precision.UUIDs often leak through other channels β emails, logs, API responses. Once an attacker has a valid UUID, IDOR allows unauthorized access if authorization checks don't exist. UUIDs are not a security control.
Developers often use UUID v4 assuming their complexity makes them "unguessable" and therefore secure. In cybersecurity, this is known as Security by Obscurity, which is not a true security layer. Once a UUID is leaked through logs, referrer headers, or other API endpoints, the system becomes vulnerable if ownership checks are missing.
Making an ID harder to guess delays the attack by minutes. A proper ownership check stops it entirely.
πΊ The Escalation Path β Read β Write β Takeover
This is the chain that turns a Medium IDOR report into a Critical one.
STEP 1 β Find a read IDOR
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
GET /api/users/4822
Authorization: Bearer <attacker_token>
Response: 200 OK
{ "id": 4822, "email": "victim@example.com" }
Severity at this point: LowβMedium
STEP 2 β Check if the same pattern holds on a write endpoint
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
PATCH /api/users/4822
Authorization: Bearer <attacker_token>
{ "email": "attacker@evil.com" }
Response: 200 OK
{ "id": 4822, "email": "attacker@evil.com" }
Severity now: High
STEP 3 β Trigger password reset for the modified email
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
POST /api/auth/reset-password
{ "email": "attacker@evil.com" }
β Reset link sent to attacker's inbox
β Attacker clicks link, sets new password
β Logs into victim's account with full access
Final severity: Critical β Full Account TakeoverSTEP 1 β Find a read IDOR
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
GET /api/users/4822
Authorization: Bearer <attacker_token>
Response: 200 OK
{ "id": 4822, "email": "victim@example.com" }
Severity at this point: LowβMedium
STEP 2 β Check if the same pattern holds on a write endpoint
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
PATCH /api/users/4822
Authorization: Bearer <attacker_token>
{ "email": "attacker@evil.com" }
Response: 200 OK
{ "id": 4822, "email": "attacker@evil.com" }
Severity now: High
STEP 3 β Trigger password reset for the modified email
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
POST /api/auth/reset-password
{ "email": "attacker@evil.com" }
β Reset link sent to attacker's inbox
β Attacker clicks link, sets new password
β Logs into victim's account with full access
Final severity: Critical β Full Account TakeoverAccount takeover: set "password": "hacked123" on another user's profile if IDOR plus mass assignment β the read bug becomes a full account takeover when write access exists on the same object.
The triage team saw a GET request that read an email address. What they missed was that the same authorization failure applied to PATCH β and that the combination produced account takeover. This is why testing every HTTP method on every vulnerable endpoint is non-negotiable.
πΈοΈ GraphQL IDOR β The Harder-to-Detect Variant
REST IDOR is well understood. GraphQL IDOR is not.
VULNERABLE GraphQL QUERY
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
query {
user(id: "4822") { β Attacker substitutes victim's ID
email
phone
privateMessages {
content
sender
}
}
}
Response: 200 OK
β Full victim profile returned, including private messages.VULNERABLE GraphQL QUERY
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
query {
user(id: "4822") { β Attacker substitutes victim's ID
email
phone
privateMessages {
content
sender
}
}
}
Response: 200 OK
β Full victim profile returned, including private messages.By altering object IDs in nested queries, unauthorized data from other users can be accessed without raising flags β making GraphQL-based IDOR harder to detect and even harder to prevent without deep introspection and schema validation.
GraphQL's flexible query structure means that a single authorization gap can expose multiple object types in one request. Automated scanners rarely test GraphQL queries for IDOR because the attack surface is query-defined rather than URL-defined.
π Real Payouts β What Impact Looks Like In Practice
Uber Eats BOLA, 2024 β Restaurant analytics endpoint accepted any restaurant ID. An authenticated attacker could access revenue, transaction volume, and financial analytics for competitor restaurants. Uber evaluated this finding as a high-severity issue and awarded a $2,000 bounty.
Project management IDOR β A project management platform used sequential IDs for projects. Any authenticated user could access any project by changing the ID. The application verified authentication but never checked whether the user was assigned to that specific project. Client information, project timelines, and financial data for every project β cross-customer.
$15,500 critical IDOR β Authorization flaws like IDOR have seen a massive spike in disclosures. According to HackerOne's latest report, they are behind some of the largest data breaches of the past two years. A write IDOR on user settings, combined with mass assignment that included role elevation, produced admin account creation β rated Critical, paid accordingly.
π‘οΈ Defensive Guidance β The Ownership Check Pattern
Every layer of defense below becomes irrelevant if the ownership check is missing. The ownership check is the only control that actually fixes IDOR.
# β WRONG β Trusts client-supplied ID, no ownership check
@app.get("/api/orders/{order_id}")
def get_order(order_id: int):
return db.query(Order).filter(Order.id == order_id).first()
# β
RIGHT β Validates ownership against authenticated session
@app.get("/api/orders/{order_id}")
def get_order(order_id: int, current_user: User = Depends(get_current_user)):
order = db.query(Order).filter(
Order.id == order_id,
Order.user_id == current_user.id # β ownership check
).first()
if not order:
raise HTTPException(status_code=403, detail="Forbidden")
return order# β WRONG β Trusts client-supplied ID, no ownership check
@app.get("/api/orders/{order_id}")
def get_order(order_id: int):
return db.query(Order).filter(Order.id == order_id).first()
# β
RIGHT β Validates ownership against authenticated session
@app.get("/api/orders/{order_id}")
def get_order(order_id: int, current_user: User = Depends(get_current_user)):
order = db.query(Order).filter(
Order.id == order_id,
Order.user_id == current_user.id # β ownership check
).first()
if not order:
raise HTTPException(status_code=403, detail="Forbidden")
return orderThe additional controls that reduce blast radius:
ControlWhat It DoesIDOR Fix?Server-side ownership checkVerifies caller owns the objectβ Yes β the only real fixUUID identifiers (v4)Makes enumeration slowerβ οΈ Defense in depth onlyResponse field allowlistLimits damage if IDOR existsβ οΈ Reduces severity, not root causeRate limiting on object endpointsSlows mass enumerationβ οΈ Limits scale, not accessCI authorization test (two accounts, assert 403)Prevents regressionβ Yes β catches re-introduction
BOLA should be treated as a design bug, not an edge case. OWASP's API1 is at the top of the list for a reason.
π TL;DR
β IDOR = authentication present, authorization absent. Ranked API1 in OWASP since 2019. β Severity is determined by the object's sensitivity, the HTTP method, and the scope of exposure β not the bug pattern alone. β UUID IDs don't fix IDOR. They leak via emails, API list responses, JS bundles, and referrer headers. β Read IDOR + write endpoint on same object = account takeover. Test all methods, not just GET. β GraphQL IDOR is the harder-to-detect modern variant. Nested query mutation = same missing ownership check. β One fix: server-side WHERE object.owner_id = current_user.id on every object request. Everything else is noise.
References
- OWASP API Security Top 10 2023 β API1:2023 BOLA / OWASP Top 10 2021 β A01: Broken Access Control
- OWASP Community β Insecure Direct Object Reference definition
- CWE-639: Authorization Bypass Through User-Controlled Key (MITRE)
- Uber Eats BOLA Writeup β $2,000 bounty, restaurant.uber.com analytics endpoint
- HackerOne 2024/2025 Annual Hacker-Powered Security Report
- CVE-2025β13526 β IDOR in OneClick Chat to Order WordPress plugin (β€ 1.0.8)
- Grafana CVE-2024β1313 β Cross-organization dashboard access via missing ownership check