The IDOR Bug Hiding in Every AI-Generated API Route
AI coding tools write routes that fetch a record by its ID and forget to check who owns it. Anyone can swap a number in the URL and read someone else's data.

Your API lets any logged-in user read someone else's data. All it takes is changing a number in the URL.
This bug has a name: IDOR, short for Insecure Direct Object Reference. It shows up in almost every app built with an AI tool. The AI writes code that fetches a record by its ID. It skips the part where it checks who owns that record.
Here's why that happens. You ask an AI tool to build an endpoint that fetches an order by ID. It writes exactly that: find the order, return it. It never asks whether the person making the request actually owns the order. The demo looks fine, because you test it as yourself. Nobody tries a different ID until a real user does, by accident or on purpose.
Here's what the broken version looks like:
app.get('/api/orders/:id', requireAuth, async (req, res) => {
const order = await db.orders.findById(req.params.id);
res.json(order);
});This route checks that someone is logged in. It never checks that the order belongs to them. Swap the ID in the URL for someone else's order, and you get their name, address, and order history back.
Here's the fix. Add the current user's ID to the query, not just the record ID:
app.get('/api/orders/:id', requireAuth, async (req, res) => {
const order = await db.orders.findOne({
_id: req.params.id,
userId: req.user.id,
});
if (!order) return res.status(404).json({ error: 'Not found' });
res.json(order);
});Now the database only returns a row that belongs to the person asking for it. A mismatch returns a 404, not someone else's private data.
This isn't just a GET problem. PATCH and DELETE routes need the same check. A route that updates or deletes a record by ID needs it too. Skip the check, and one user can edit or wipe another user's data. Check three things on every route that takes an ID:
- Does the query filter by the logged-in user, not just the record ID?
- Does a missing or mismatched record return 404, not a raw database error?
- Does this apply to every method: GET, PATCH, PUT, and DELETE?
To check your own app, open your API routes. Find every place you fetch, update, or delete a record using an ID from the URL. For each one, check whether the query also filters by the current user. Then log in as two test accounts and swap IDs between them in the browser's network tab. If one account can see or change the other's data, you have an IDOR.
If you want someone else to check this for you, get a free assessment. We'll scan your app's routes and tell you which ones are exposed, within 24 hours.
VibeAudits audits apps built with Cursor, Lovable, Bolt, Claude Code, Replit, and other AI tools.