← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

Insecure Direct Object Reference (IDOR)

Security CWE-639 OWASP A1:2021 CVSS 7.5 PHP 5.0+ Intermediate
debt(d9/e3/b5/t7)
d9 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'silent in production until users hit it' (d9). The detection_hints explicitly state automated: no, and the why_it_matters field confirms it is 'invisible to automated scanners.' Semgrep is listed as a tool but the code_pattern requires detecting the absence of a subsequent ownership check — a logic-level property that static analysis cannot reliably verify. IDOR produces no error, no log anomaly, and no visible failure; it only manifests when an attacker actually accesses another user's data, typically discovered via manual penetration testing or user complaint.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix is a single ownership assertion ($resource->user_id === auth()->id()) added after every resource load. While one endpoint is a one-liner, the common_mistakes note that update and delete routes are commonly missed, meaning the fix must be applied as a pattern across all resource-loading endpoints — a small but consistent refactor within one component or controller layer, not a cross-cutting architectural change.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5). IDOR applies broadly to all web and API contexts (applies_to: web, api) and the required ownership-check pattern must be enforced on every resource-fetching route indefinitely. Every new endpoint a developer writes carries the same risk and requires the same discipline, imposing an ongoing productivity tax across many work streams, though it does not reshape the system's architecture.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap — contradicts how a similar concept works elsewhere' (t7). The misconception field directly captures the canonical trap: developers substitute UUID randomness for authorisation, believing obscurity equals security. Additionally, common_mistakes show that developers routinely conflate authentication (is the user logged in?) with authorisation (does this user own this resource?), a distinction that contradicts the mental model formed from simpler authentication-only systems. Both traps are well-documented yet consistently repeated by competent developers.

About DEBT scoring →

Also Known As

Insecure Direct Object Reference object reference vulnerability

TL;DR

A user accesses another user's data by changing an ID in a URL or request — no authorisation check performed.

Explanation

IDOR occurs when an application exposes internal object identifiers (database IDs, filenames) directly in requests and does not verify that the requesting user is authorised to access that object. Example: /invoice?id=1234 — incrementing the ID to 1235 returns another user's invoice. Prevention: always verify that the authenticated user owns or has permission to access the requested resource, server-side, on every request.

How It's Exploited

GET /api/orders/1337
# If order 1337 belongs to another user and there's no ownership check,
# attacker reads their private data by incrementing the ID

Diagram

flowchart TD
    USER2[Logged in as User 42] --> REQ3[GET /api/invoices/100]
    REQ3 --> CHECK2{Check: does user 42<br/>own invoice 100?}
    CHECK2 -->|no check!| RETURN2[Returns invoice 100<br/>belongs to user 99!]
    subgraph Fix3
        AUTH3[Authorisation check<br/>SELECT WHERE id=100 AND user_id=42]
        ZERO[0 rows = 404 Not Found<br/>never reveal resource exists]
    end
style CHECK2 fill:#f85149,color:#fff
style RETURN2 fill:#f85149,color:#fff
style AUTH3 fill:#238636,color:#fff

Common Misconception

Using UUIDs instead of sequential IDs prevents IDOR. UUIDs prevent guessing but not IDOR — if authorisation checks are missing, an attacker who obtains any valid UUID can still access it freely.

Why It Matters

IDOR lets attackers access any user's data by simply changing an ID in the URL or request body. It is one of the most common API vulnerabilities because it is invisible to automated scanners and requires no special tools to exploit.

Common Mistakes

  • Assuming sequential IDs are "hard to guess" — automated tools enumerate thousands of IDs per second.
  • Checking authentication but not ownership — verifying a user is logged in does not verify they own the resource.
  • Using UUIDs and believing that makes IDOR impossible — obscurity without authorisation checks is not security.
  • Only protecting read endpoints and forgetting update and delete routes.

Code Examples

✗ Vulnerable
// No ownership check — any user can view any order
public function show(int $orderId): JsonResponse {
    return response()->json(Order::findOrFail($orderId));
}
✓ Fixed
public function show(int $orderId): JsonResponse {
    $order = Order::findOrFail($orderId);

    // Enforce ownership — compare to authenticated user
    if ($order->user_id !== auth()->id()) {
        abort(403); // Forbidden — not just 404
    }

    return response()->json($order);
}

// Or scope query to current user (prevents the object being fetched at all)
$order = auth()->user()->orders()->findOrFail($orderId);

Added 13 Mar 2026
Edited 12 Jun 2026
Views 64
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 1 ping T 0 pings F 0 pings S 0 pings S 1 ping M 0 pings T 1 ping W 1 ping T 3 pings F 2 pings S 0 pings S 0 pings M 2 pings T 4 pings W 0 pings T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W
No pings yet today
No pings yesterday
Ahrefs 11 Scrapy 11 Perplexity 9 Amazonbot 8 Unknown AI 3 SEMrush 3 Google 2 Claude 2 Bing 2 ChatGPT 2 Majestic 1 Meta AI 1
crawler 51 crawler_json 3 pre-tracking 1
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Low
⚡ Quick Fix
After loading a resource by ID, always check: $resource->user_id === auth()->id() — never trust that the ID alone grants access
📦 Applies To
PHP 5.0+ web api
🔗 Prerequisites
🔍 Detection Hints
->find($_GET['id']) or findOrFail($id) with no subsequent ownership check
Auto-detectable: ✗ No semgrep
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: High Context: File Tests: Update
CWE-284 CWE-639


✓ schema.org compliant