SecurityBlog Post

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.

By Abhay Mittal

3 min read

Updated

The IDOR Bug Hiding in Every AI-Generated API Route
Is your AI-built app exposed? Get a professional vibe coding audit and ship to production with confidence.

An API route can expose another user's private data when it checks login but omits object-level authorization. Changing the record ID is enough to reveal the missing check.

This bug has a name: IDOR, short for Insecure Direct Object Reference. It can affect code written by people or AI tools. 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.

The missing ownership check

Here's what a vulnerable route 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.

Scope the query to the authenticated user

Add the current user's ID to the query, not just the record ID. These simplified examples assume your authentication middleware has already verified the session:

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?

Test with two accounts

Use an authorized staging environment and accounts you control. 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.

For multi-tenant apps, verify membership and roles in the requested organisation as well as record ownership. UUIDs make IDs harder to guess but do not replace permission checks. See the OWASP IDOR prevention guide and our Supabase RLS walkthrough.

If you want someone else to check this for you, book an audit call. We'll discuss your app's routes, scope the review, and provide a fixed-price quote.

VibeAudits audits apps built with Cursor, Lovable, Bolt, Claude Code, Replit, and other AI tools.

Keep reviewing your app

Practical checks for the parts of an AI-built app that handle real users and money.

Need a second pair of eyes? Explore our code audit services, scope and pricing, and client case studies.

VibeAudits

Security Experts

Worried your vibe-coded app has issues like this?

We run professional code audits for SaaS apps and AI features built with Cursor, Claude, Copilot, Lovable and Replit. We find the security and reliability problems before your customers (or attackers) do, then hand you a fix-ready report.