What is IDOR and how to prevent insecure direct object references
IDOR mechanics, direct object references, ownership validation, indirect references, UUID vs sequential ID risks, authorization checks vs authentication
Insecure Direct Object Reference (IDOR)
IDOR occurs when an application exposes internal object identifiers and doesn't verify that the requesting user owns or has access to that object. It's the most common access control flaw and often leads to mass data exposure.
The Vulnerability
// VULNERABLE — no ownership check
app.get('/api/invoices/:id', authenticate, async (req, res) => {
const invoice = await Invoice.findById(req.params.id);
// Attacker changes id=1001 to id=1002 — gets another user's invoice
res.json(invoice);
});The Fix: Always Scope by Authenticated User
// SAFE — ownership enforced in the query
app.get('/api/invoices/:id', authenticate, async (req, res) => {
const invoice = await Invoice.findOne({
_id: req.params.id,
userId: req.user.id // Only returns if this user owns it
});
if (!invoice) {
// Return 404, not 403 — don't confirm the resource exists
return res.status(404).json({ error: 'Invoice not found' });
}
res.json(invoice);
});Sequential IDs Make IDOR Worse
If your IDs are sequential integers (1, 2, 3...), an attacker can trivially enumerate all resources. Using UUIDs doesn't fix the authorization flaw—it just makes enumeration harder. The fix is always an ownership check, not obscured IDs. But UUIDs are still better than sequential integers as a secondary defense.
Beyond Ownership: Role Checks
Not all resources are owner-scoped. Admin endpoints need role checks: verify req.user.role === 'admin' on every admin route, not just once at login. A user who is downgraded from admin should immediately lose access.
