Anatomy of an IDOR: how we detect broken object-level authorization
Insecure Direct Object References — IDORs — are the single most common API security vulnerability. OWASP ranks Broken Object Level Authorization (BOLA) as the #1 risk in the API Security Top 10. And yet most automated scanners completely miss them.
The reason is simple: detecting an IDOR requires context. You need two user sessions, you need to identify which requests carry object identifiers, and you need to understand whether the response should differ between users. That's hard to automate. We did it anyway.
What is an IDOR?
An IDOR occurs when an application exposes a direct reference to an internal object — a database ID, a filename, a record key — and doesn't verify that the requesting user is authorized to access it.
The classic example:
# User A views their order
GET /api/orders/1001 → 200 (User A's order)
# User B requests User A's order
GET /api/orders/1001 → 200 (User A's order!)
User B shouldn't see User A's order. But the server didn't check — it just returned whatever ID was requested. This is an IDOR.
Our detection approach
BrokenApp detects IDORs in four steps:
1. Capture two sessions
Run two scans — one as User A, one as User B. Each scan captures every HTTP request with full headers, cookies, and response bodies.
2. Identify ID-bearing requests
Parse every URL, query parameter, and JSON body for values that look like object identifiers: numeric IDs, UUIDs, slugs. We use pattern matching, not guessing.
3. Cross-user replay
For every ID-bearing request from User A, replay it with User B's session credentials. Record the response status code and body.
4. Classify the response
Compare the replayed response to the original. If User B gets a 200 with a body that matches User A's response (body similarity > 80%), it's a confirmed IDOR.
Body similarity scoring
The naive approach — checking if the status code is 200 — produces massive false positives. Public endpoints return 200 for everyone. Error pages return 200 with generic HTML. You need to compare the actual content.
BrokenApp computes a similarity score between the original response body and the replayed response body. We normalize JSON responses (sort keys, strip timestamps) and compare structural similarity. A score above 80% with matching status codes is a confirmed IDOR. Between 50-80% is flagged for manual review.
Scaling to N×N with auth matrix
Two-user testing is a start, but real applications have multiple roles: admin, editor, viewer, anonymous. A two-user scan misses the editor-to-admin vector, the anonymous-to-viewer vector, and every other combination.
BrokenApp's auth matrix mode tests every role pair. Define your roles in a TOML file, run the scan, and get a full N×N matrix showing exactly which role pairs have authorization failures and on which endpoints.
$ brokenapp auth-matrix --roles roles.toml
admin editor viewer anon
admin — 4 7 2
editor 0 — 3 1
viewer 0 0 — 0
✓ 17 IDORs across 12 pairs
Each cell in the matrix shows how many endpoints are accessible by the row role when they should be restricted to the column role. The admin column having high numbers means admin endpoints are leaking to lower-privilege roles — exactly what you'd expect to find and exactly what needs fixing.
Severity classification
Not all IDORs are equal. BrokenApp auto-classifies severity:
- Critical — Write access (POST/PUT/DELETE) to another user's resources
- High — Read access to PII or sensitive data (emails, payment info, settings)
- Medium — Read access to non-sensitive data (public profiles, metadata)
- Low — Read access to data that's arguably semi-public
Every finding is tagged with CWE-639 (Authorization Bypass Through User-Controlled Key) and mapped to OWASP API Security Top 10 A01 (Broken Object Level Authorization).
Try it yourself
Install the CLI, run two scans with different user sessions, and point BrokenApp at the results. The entire IDOR detection pipeline runs locally in seconds.
$ brokenapp scan --url https://yourapp.com --user admin
$ brokenapp scan --url https://yourapp.com --user viewer
$ brokenapp idor-scan --scan-a ./admin --scan-b ./viewer