Insecure Direct Object Reference
Insecure Direct Object Reference is when code accesses a restricted resource based on user input, but fails to verify user’s authorization to access that resource. Put another way: there exists a “direct reference” to an “object” which is “insecure”.
IDOR falls under A01:2025 – Broken Access Control in the OWASP Top 10, the highest-ranked vulnerability category.
It is easiest to understand Insecure Direct Object Reference by seeing an example and how it can be exploited.
Imagine that a user clicks on a link to view an invoice.
The code that accesses the invoice might look something like:
<?php
$sql = "SELECT * FROM orders WHERE invoice_id='{$invoice}'";
$result = mysqli_query($connection, $sql);
?>
Aside: the snippet above also concatenates
$invoicedirectly into the SQL string, so it is vulnerable to SQL injection in addition to IDOR. A real implementation would use a prepared statement; the IDOR illustration here keeps the inline form so the access-control point is not obscured.
This is the “direct object reference”. A user has direct access to the invoice. The invoice ID goes directly into the SQL and returns the invoice. It is insecure because there is never any verification that the user is authorized to view the invoice. Not only could anyone use the same URL to access this invoice, but entering other invoice IDs such as “TPX-10456” would most likely provide insecure access to other invoices. These invoices might reveal someone else’s name, address, phone, email, credit card, items ordered, and more.
Examples of types of resources which could have insecure direct references are:
- Database data
- Files
- Directories
- Scripts
- Functions
If a user can trigger what should be a privileged result without having to validate their privileges, then it is probably a case of Insecure Direct Object Reference.
Insecure Direct Object Reference Preventions
The primary prevention is to validate a user’s authorization before allowing access to a privileged resource. Require a user to log in, and confirm that logged in users have sufficient access privileges.
In some situations, it may be possible to allowlist the acceptable input and reject input which is not allowed.
As a defense-in-depth measure, the direct object reference can be replaced with an indirect reference — for example, by providing a per-user list of choices that are meaningful only in that user’s context. The OWASP IDOR Prevention Cheat Sheet is explicit that this is supplementary: “use complex identifiers as a defense-in-depth measure, but remember that access control is crucial even with these identifiers.” Treat indirection as one extra layer; the authorization check above is still the load-bearing protection.
Example
- Logged in user sees list of 4 previous orders
- Previous orders are numbered 1-4
- User chooses order #2
- The request sends ID=‘2’, not the order ID
- ID is indirect, only meaningful in user’s scope
In the above example, the only valid choices for the user are 1-4. The server-side handler must still confirm that ID=‘2’ resolves to an order the requesting user owns before returning it — without that check, even an indirect ID can be tampered with (e.g. to ID=‘5’) and expose another user’s data.