Insecure Direct Object Reference (IDOR) Vulnerabilities and Protection
Description:
Insecure Direct Object Reference (IDOR) is a common access control vulnerability that occurs when an application directly uses user-supplied input (such as URL parameters, form fields) to access internal objects (e.g., database records, files, account data). Attackers can modify parameter values (for example, changing user_id=123 to user_id=124) to access other users' sensitive data without authorization or perform unauthorized operations. For instance, in an online banking application that queries accounts directly via /account?number=888, an attacker could steal information by enumerating account numbers.
Analysis Process:
-
Understanding the Core Vulnerability:
- The root cause is the lack of permission verification. The application assumes the user-supplied parameters (e.g., IDs) are legitimate but does not check "whether the current user is authorized to access the resource corresponding to that ID."
- Key distinction: The parameter value itself might be valid (e.g.,
124is a real account), but the access attempt should be forbidden.
-
Methods for Discovering Vulnerabilities:
- Testing Steps:
- After logging into the system, observe object identifiers (e.g., numbers, filenames) in URLs, cookies, or API requests.
- Modify the identifier (e.g., increment the ID, try other usernames) and check if unauthorized data is returned.
- Attempt to change regular user parameters to administrator parameters (e.g.,
role=admin) to check for privilege escalation vulnerabilities.
- Example:
- Normal request:
GET /api/invoice?id=100(User A accesses their own invoice). - Attack request:
GET /api/invoice?id=101(User A accesses User B's invoice without authorization).
- Normal request:
- Testing Steps:
-
Protection Design:
- Principle: Every request must undergo authorization verification, rather than relying on "hidden" parameters or front-end controls.
- Core Steps:
- Authentication: First, confirm the user's identity (e.g., via Session or Token).
- Mapping and Verification: Establish a "user-accessible object" mapping, for example:
- Associate user IDs in database queries:
SELECT * FROM invoices WHERE id = ? AND user_id = ?; - Or use Access Control Lists (ACLs) to verify permissions.
- Associate user IDs in database queries:
- Indirect References: Avoid exposing internal identifiers (e.g., database auto-increment IDs) directly. Use random, unpredictable tokens (e.g., UUIDs) or encrypted parameters instead.
- Standardized Error Handling: Unauthorized requests should return 403 Forbidden rather than 404 to avoid information disclosure.
-
Practical Code Comparison:
- Vulnerable Code (using user input directly):
invoice_id = request.GET.get('id') invoice = Invoice.objects.get(id=invoice_id) # No verification of current user's permissions - Fixed Code (associating user context):
invoice = Invoice.objects.get(id=invoice_id, user=request.user) # Automatically filters by permission if not invoice: return HttpResponse("Forbidden", status=403)
- Vulnerable Code (using user input directly):
-
Advanced Protective Measures:
- Layered Verification: Combine role-based permissions (e.g., admin, regular user) with data-level permissions (e.g., users can only operate on their own data).
- Logging and Monitoring: Log all sensitive operations and detect abnormal access patterns (e.g., the same user frequently accessing different IDs).
- Automated Testing: Use tools (e.g., Burp Suite) to scan for IDOR vulnerabilities or write unit tests to simulate unauthorized requests.
Summary:
The core of protecting against IDOR vulnerabilities is enforcing permission verification for every access. By implementing "user-resource" binding, indirect references, and the principle of least privilege, we can ensure parameters cannot be abused. During code reviews, special attention must be paid to verifying whether all object access logic that relies on user input includes permission checks.