Insecure Direct Object Reference (IDOR)
1. Definition
Insecure Direct Object Reference (IDOR) occurs when an application provides direct access to objects based on user-supplied input (like a Database ID in a URL) but fails to check if the authenticated user is authorized to access that specific object.
In the OWASP API Security Top 10, this is now classified as BOLA (Broken Object Level Authorization).
2. Technical Explanation
Consider a URL: https://api.site.com/invoices/1001. The backend code might look like:
app.get('/invoices/:id', (req, res) => {
// Vulnerable: Checks if user is logged in, BUT NOT if they own invoice #1001
if (!req.session.user) return res.status(401);
const invoice = db.find(req.params.id);
res.json(invoice);
});An attacker simply changes 1001 to 1002, 1003, etc., to download every invoice in the system. The “Direct Object Reference” is the ID 1001. The “Insecurity” is the lack of an ownership check.
3. Attack Flow
sequenceDiagram
participant Attacker
participant API
participant DB
Attacker->>API: GET /invoices/1001 (Attacker's own ID)
API->>DB: Query ID 1001
DB-->>API: Returns Invoice 1001
API-->>Attacker: 200 OK (Displays Invoice)
Note over Attacker: Attacker notices the sequential ID.
Attacker->>API: GET /invoices/1002 (Victim's ID)
API->>DB: Query ID 1002
Note right of API: App fails to check<br/>"Does User A own Invoice 1002?"
DB-->>API: Returns Invoice 1002
API-->>Attacker: 200 OK (Data Leak)4. Real-World Case Study: First American Financial (2019)
Target: First American Financial Corp (Real Estate Title Insurance). Vulnerability Class: IDOR / BOLA. Data Exposed: 885 Million Records.
The Vulnerability: The website served document images via a URL structure like: fastweb.firstam.com/documents/view?docId=1234567 There was no authentication required (or weak auth) to access the document if you knew the ID. Crucially, the Document IDs were sequential.
The Attack: A real estate developer noticed that changing the ID in the URL allowed him to see other people’s tax records, wire transaction receipts, and driver’s license images. This simple enumeration allowed access to nearly 1 billion sensitive financial records dating back to 2003.
Impact: Massive reputational damage and regulatory fines. It is a textbook example of how a simple sequential ID can lead to a catastrophic breach.
5. Detailed Defense Strategies
A. Authorization Check on Every Access
This is the only root fix. Never assume that because a user is logged in, they have access.
Logic:
const invoice = db.find(id); if (invoice.ownerId !== req.session.userId) { throw new ForbiddenError(); }Middleware: Use policy middleware (e.g., CASL, CanCan) to enforce “Resource Ownership” rules centrally.
B. Use Indirect or Random References (UUIDs)
Do not use auto-incrementing Integers (1, 2, 3) for public IDs.
- UUIDs: Use GUIDs/UUIDs (e.g.,
550e8400-e29b-41d4-a716-446655440000). They are impossible to guess (enumerate). - Map: Map internal IDs (1) to external random tokens (abc-xyz) in a session lookup table.
- Note: UUIDs prevent enumeration, but they do not fix the underlying permission issue. If an attacker leaks a UUID, they can still access it if auth is missing. Defense A is still required.
C. Automated Testing
IDORs are hard for scanners to find because scanners don’t understand “Business Logic” (who owns what).
- Write integration tests that explicitly try to access User A’s resource while logged in as User B.
