IDOR Vulnerability Fix: What It Is and How to Fix It
OverMCP Team
TL;DR: Insecure Direct Object Reference (IDOR) is a vulnerability where an attacker can access or modify resources (like user data, orders, or documents) by simply changing an ID parameter in the URL or request. To fix it, implement proper access control checks on every server-side endpoint that accesses a resource by ID—never trust user-supplied identifiers without verifying ownership.
What Is an IDOR Vulnerability?
IDOR stands for Insecure Direct Object Reference. It occurs when an application exposes a reference to an internal object (like a database ID, filename, or key) and fails to verify that the user is authorized to access that specific object. For example, if your app uses /api/order/123 to fetch order details, an attacker can change 123 to 124 and view another user's order if no access control is in place.
IDOR is one of the most common vulnerabilities in vibe-coded apps because AI tools often generate CRUD endpoints that trust the user-supplied identifier without checking permissions. It falls under the OWASP Top 10 category "Broken Access Control."
How IDOR Exploits Work (Real Examples)
Example 1: Numeric ID in URL
Imagine a note-taking app with this endpoint:
GET /note/42If the server returns note 42 without checking if the current user owns it, an attacker can iterate through IDs to read every note:
GET /note/1
GET /note/2
...
GET /note/1000Example 2: UUID or Hash in URL
Even if you use UUIDs, IDOR can exist if the endpoint doesn't verify ownership. For instance:
GET /api/document/abc-123-defIf the attacker can guess or obtain another user's document UUID (e.g., via a shared link or leaked in client-side code), they can access it.
Example 3: ID in Request Body or Query Parameter
IDOR isn't limited to URLs. Consider a POST endpoint to update a user profile:
POST /api/user/update
{
"user_id": 456,
"email": "hacker@evil.com"
}If the server uses user_id from the request body instead of the authenticated user's ID, an attacker can change any user's email.
How to Fix IDOR Vulnerabilities (Step-by-Step)
Step 1: Authenticate Every Request
Ensure every endpoint that accesses a resource requires authentication. Use session tokens, JWTs, or API keys to identify the user.
Step 2: Implement Authorization Checks
For every endpoint that uses an ID to fetch or modify a resource, verify that the authenticated user owns or has permission to access that resource.
#### Fix for Example 1 (Numeric ID)
Vulnerable code (Node.js/Express):
app.get('/note/:id', async (req, res) => {
const note = await db.findNoteById(req.params.id);
res.json(note);
});Fixed code:
app.get('/note/:id', async (req, res) => {
const note = await db.findNoteById(req.params.id);
if (!note) return res.status(404).json({ error: 'Note not found' });
if (note.userId !== req.user.id) {
return res.status(403).json({ error: 'Forbidden' });
}
res.json(note);
});#### Fix for Example 2 (UUID)
Same principle—check ownership:
app.get('/document/:uuid', async (req, res) => {
const doc = await db.findDocumentByUuid(req.params.uuid);
if (!doc) return res.status(404).json({ error: 'Document not found' });
if (doc.ownerId !== req.user.id) {
return res.status(403).json({ error: 'Forbidden' });
}
res.json(doc);
});#### Fix for Example 3 (ID in Body)
Never trust the client-provided user ID. Use the authenticated user's ID from the session/token:
app.post('/user/update', async (req, res) => {
const userId = req.user.id; // from auth middleware
const { email } = req.body;
// Update user where id = userId
await db.updateUser(userId, { email });
res.json({ success: true });
});Step 3: Use Indirect References (Optional)
Instead of exposing internal database IDs, use indirect references that are unique per user and hard to guess. For example, assign each user a random token for each resource:
GET /order/token-xyz789But this is a band-aid—you still need authorization checks.
Step 4: Test for IDOR
Manual testing:
Automated scanning: Use tools like Burp Suite, OWASP ZAP, or OverMCP's security scanner to detect IDOR in your app.
IDOR in Vibe-Coded Apps: Why It's Common
AI coding tools like Cursor, Bolt.new, and Lovable generate code quickly, but they often omit access control logic. They assume the developer will add it later—or worse, they generate endpoints that trust user input implicitly. If you're building an MVP with AI, you must manually audit every endpoint that takes an ID parameter.
Preventing IDOR at the Architecture Level
const note = await db.findNote({ id: req.params.id, userId: req.user.id });
if (!note) return res.status(404).json({ error: 'Note not found' });This eliminates the risk of forgetting the ownership check.
Tools to Help You Find IDOR
FAQ
What is the difference between IDOR and broken access control?
IDOR is a specific type of broken access control. Broken access control is a broader category that includes any failure to enforce proper permissions (e.g., privilege escalation, missing function-level access control). IDOR specifically involves direct access to objects via user-supplied identifiers.
Can IDOR exist if I use UUIDs instead of sequential IDs?
Yes. UUIDs make guessing harder but do not prevent IDOR. If an attacker obtains a valid UUID (e.g., via a shared link, data breach, or client-side leak), they can still access the resource if no ownership check is performed.
How do I test my app for IDOR vulnerabilities?
Log in as two different users. Take a resource ID from user A's session (e.g., from the URL or API response) and try to access that resource while logged in as user B. If the request succeeds, you have an IDOR vulnerability. Use automated scanners like OverMCP to find them across your entire app.