Security Cipher

Security Resources

⌘K
  1. Home
  2. Security Resources
  3. Vulnerability Explain
  4. Insecure Direct Object References (IDOR)

Insecure Direct Object References (IDOR)

Welcome back to the Vulnerability Explain series. This time we are unpacking Insecure Direct Object References, better known as IDOR. It is one of the most common findings in bug bounty reports, and the reason is simple – it is easy to introduce and easy to miss. We will look at what IDOR really is, how attackers abuse it, how to test for it, and how to shut it down for good. Let’s get into it.

What is IDOR?

IDOR happens when an application exposes a reference to an internal object – like a database row, a file, or an account – and then trusts whatever value the user sends without checking that the user is actually allowed to touch that object.

Picture an invoice page at /invoice?id=1024. You change the number to 1023 and suddenly you are reading someone else’s invoice. The server happily returned it because it only asked “does this id exist?” and never asked “is this id yours?”. That missing authorization check is the whole bug.

How does IDOR work?

At its heart IDOR is a broken access control problem. Here is the typical chain of events that lets an attacker walk straight into other people’s data:

  • Object Reference Exposure
    • The application puts a direct identifier (numeric id, username, filename, UUID) in a URL, form field, API path, or cookie.
  • Predictable or Enumerable Values
    • These identifiers are sequential or easy to guess, so an attacker can simply increment or fuzz them to discover other objects.
  • Missing Authorization Check
    • The server validates that the user is logged in but forgets to confirm the logged-in user owns the requested object.
  • Unauthorized Access
    • The attacker reads, edits, or deletes records that belong to other users – orders, messages, documents, or even admin settings.
  • Impact and Escalation
    • Depending on the endpoint, IDOR can leak personal data, allow account takeover, or let a normal user perform privileged actions.

In short, IDOR is the application handing you the keys to a locker and never checking whether the locker is yours. The fix is always about authorization, not about hiding the id.

IDOR attack flow diagram showing an attacker changing an invoice id to access another user's data

How an IDOR attack works: changing the object reference (id) to read another user’s data.

Tools and Techniques for IDOR Testing

Finding IDOR is mostly a manual, brain-driven exercise, but a few tools make the grind of swapping and replaying identifiers far less painful.

Manual Testing Methodologies

  • Parameter Swapping – Create two accounts, capture a request from account A, then replay it with account B’s session and watch whether account A’s data still comes back.
  • ID Enumeration – Increment, decrement, or fuzz numeric and UUID identifiers in URLs, JSON bodies, and headers to see if other objects are returned.
  • HTTP Method Tampering – Try GET, POST, PUT, and DELETE on the same object reference; access control is often enforced on one verb but not the others.
  • Hidden Field and API Review – Inspect mobile and single-page-app API calls where object ids are passed directly and rarely re-checked server side.

Automated Scanning Tools

  • Burp Suite Intruder – Automates id enumeration and lets you diff responses to spot unauthorized data.
  • Autorize – A Burp extension that replays every request with a low-privilege session and flags access control gaps automatically.
  • OWASP ZAP – Free proxy with an access control testing add-on for comparing responses across user roles.
  • ffuf – Fast fuzzer for brute-forcing identifiers and discovering reachable object references.

IDOR Protection Mechanisms

Best Practices for Secure Coding

  • Enforce Object-Level Authorization
    • Description: On every request, verify the authenticated user owns or may access the requested object.
    • Benefits: Removes the root cause of IDOR entirely.
    • Implementation Tip: Scope every query by user id, for example WHERE id = ? AND owner_id = current_user.
  • Use Indirect Reference Maps
    • Description: Map real database ids to per-session random tokens that mean nothing outside that session.
    • Benefits: Stops attackers from guessing or enumerating real identifiers.
    • Implementation Tip: Generate a fresh lookup table per user session.
  • Prefer Unpredictable Identifiers
    • Description: Replace sequential integers with random UUIDs where practical.
    • Benefits: Makes enumeration far harder, though it is defense in depth, not a substitute for authorization.
    • Implementation Tip: Never rely on obscurity alone – always pair it with a real ownership check.

Best Practices for Organizations

  • Centralized Access Control
    • Build authorization into a shared middleware or policy layer instead of scattering checks across controllers.
    • Review every new endpoint against that policy before release.
  • Security Testing in CI
    • Add automated access-control regression tests that run with multiple user roles.
    • Fail the build when a low-privilege user can reach high-privilege data.
  • Bug Bounty and Pentesting
    • IDOR is a top reward category – invite researchers to hunt for it.
    • Schedule recurring manual reviews of object-level permissions.

Top IDOR payloads used by Security Researchers

As a security researcher, knowing the most common payloads helps you detect and prevent these attacks. Use this knowledge ethically and only on systems you are authorized to test. Some sample payloads are shown below.

// Numeric id enumeration
GET /api/v1/orders/1024
GET /api/v1/orders/1023
GET /api/v1/orders/1
// Swapping references in a JSON body
POST /api/v1/account/update
{"user_id": 5012, "email": "attacker@evil.com"}
// Method tampering on the same object
GET    /api/v1/files/87f3
DELETE /api/v1/files/87f3
PUT    /api/v1/files/87f3
// File reference abuse
GET /download?file=user_5012_report.pdf
GET /download?file=user_0001_report.pdf

Real-World Example: Account Data Leak via Sequential IDs

A SaaS billing portal let users download invoices from /portal/invoice/.pdf. The ids were simple incrementing integers and the server only checked that a valid session cookie was present, never that the invoice belonged to the requester.

A researcher logged in with a normal account, noticed their invoice id was 40192, and wrote a short script to walk backwards through the range. Within minutes they were pulling invoices belonging to thousands of other customers, complete with names, addresses, and partial payment details.

The fix was a single ownership check at the data layer that tied each invoice to its account id. The lesson is the one IDOR always teaches: authenticate the user, but never forget to authorize the object.

Vulnerable and secure code of IDOR

The following example shows the contrast between vulnerable and secure code for IDOR. It helps you see how the flaw creeps into real code and the changes that shut it down.

🥺 Vulnerable Code:

# Vulnerable: trusts the id from the request, no ownership check
@app.route("/api/invoice/<int:invoice_id>")
def get_invoice(invoice_id):
    user = get_logged_in_user()
    # Only checks that the user is logged in, never that the
    # invoice belongs to them.
    invoice = db.query("SELECT * FROM invoices WHERE id = ?", invoice_id)
    return jsonify(invoice)
  • The endpoint authenticates the user but never authorizes the object.
  • Any logged-in user can read any invoice simply by changing invoice_id.

😎 Secure Code:

# Secure: scope every query to the authenticated user
@app.route("/api/invoice/<int:invoice_id>")
def get_invoice(invoice_id):
    user = get_logged_in_user()
    invoice = db.query(
        "SELECT * FROM invoices WHERE id = ? AND owner_id = ?",
        invoice_id, user.id,
    )
    if not invoice:
        abort(404)  # do not reveal that the object exists
    return jsonify(invoice)
  • The query is constrained by owner_id, so users only ever see their own records.
  • Returning 404 instead of 403 avoids leaking that the object exists.

Conclusion

IDOR is a deceptively simple bug with serious consequences. It comes down to one missing question – not “is this user logged in?” but “is this object theirs?”. By enforcing object-level authorization on every request, scoping queries to the current user, and preferring unpredictable identifiers as defense in depth, you remove the flaw at its root. Authentication tells you who someone is; authorization decides what they may touch, and IDOR lives entirely in that gap.

How can we help?