IDOR in AI-built web apps (authorization): how to catch it before you ship
securitywebai

IDOR in AI-built web apps (authorization): how to catch it before you ship

4 min read

AI-assisted development ships fast, and that makes authorization regressions common. This practical guide shows how to detect and fix IDOR (broken object-level authorization) with a repeatable review, tests, and rollout-safe patterns.

Table of Contents

How do you prevent IDOR in AI-built web apps before it reaches production?

Conclusion

IDOR (Insecure Direct Object Reference) happens when changing an id lets a valid user access someone else’s resource.

In AI-built apps it’s common because the model optimizes for “feature works” and leaves authorization as an implied detail.

The practical fix is boring and repeatable:

  1. enforce authorization server-side for every resource fetch/mutation
  2. standardize deny behavior (403 vs 404) and logging
  3. add a regression test: “User B cannot access User A’s object”

If you can do only one thing: write that test.

Implementation examples may be available on DevSnips.

Explanation

Authentication answers “who are you?” Authorization answers “what are you allowed to access?”

IDOR is when authn exists, but authz is missing or inconsistent. AI-assisted development increases risk because:

  • generated handlers often call findById(id) and return results
  • UI-driven reasoning (“button hidden”) leaks into backend assumptions
  • multi-tenant boundaries (org/team/project) are easy to forget

“UUIDs make it safe” is not a defense. IDs leak via logs, links, screenshots, and support workflows.

Practical Guide

Step 1: identify your object boundaries (what must be protected)

List resource types that are user/org-scoped:

  • projects, documents, invoices
  • messages, threads, comments
  • files, exports, reports

If it contains personal or business data, treat it as scoped.

Step 2: inventory every id entry point (fast audit)

Search for:

  • /:id routes
  • request bodies containing id, userId, orgId, projectId
  • server actions / internal service calls

Decision rule:

  • If the client can send an id, you must assume it can be tampered with.

Step 3: enforce one authorization pattern everywhere

For every handler, do the same sequence:

  1. authenticate
  2. fetch by id
  3. authorize against the fetched object
  4. respond

Example pseudocode:

const user = await requireUser(req); // 401 if missing
const doc = await db.doc.findUnique({ where: { id: params.id } });
if (!doc) return notFound();

// authorize by ownership/org boundary
if (doc.orgId !== user.orgId) return deny();

return json(doc);

Step 4: pick deny semantics (403 or 404) and be consistent

  • Prefer 404 if you want to reduce object enumeration
  • Prefer 403 if you want explicit denials for auditing

Pick one for the whole app, document it, and reuse helpers.

Step 5: add the IDOR regression test (the highest ROI)

Minimal test shape:

  • create object as User A
  • try to fetch/update it as User B
  • expect 403/404

Decision rule:

  • If the test is hard to write, your auth boundary is not centralized enough.

Step 6: add logging without leaking PII

Log the denial event with:

  • user id / org id (internal ids)
  • route + method
  • object type

Do not log raw tokens, emails, or full payloads.

Pitfalls

  • “We hide it in the UI” (not a control)
  • trusting orgId / userId sent by the client
  • inconsistent deny behavior (some routes 403, others 200)
  • forgetting background jobs/webhooks that mutate by id
  • logging too much and creating a data leak

Checklist

  • [ ] Resource types are classified as user/org scoped
  • [ ] Every endpoint/action that accepts an id is inventoried
  • [ ] Auth pattern is consistent (authenticate → fetch → authorize → respond)
  • [ ] Client-provided orgId/userId is never trusted as authorization
  • [ ] Deny semantics are standardized (403 vs 404)
  • [ ] A shared helper exists for deny responses
  • [ ] IDOR regression test exists (User B cannot access User A’s object)
  • [ ] Multi-tenant boundaries are enforced (org/team/project)
  • [ ] Webhooks/cron/batch jobs follow the same authz rules
  • [ ] Denial logging exists without leaking PII/secrets
  • [ ] Review gates exist for AI-generated code touching authz

FAQ

Q1. Are UUIDs enough to prevent IDOR?

No. UUIDs reduce guessing, but IDs still leak via links, logs, screenshots, and support workflows. Authorization must be enforced server-side.

Q2. Should I return 403 or 404 on unauthorized access?

Either can be correct. Use 404 to reduce enumeration. Use 403 to support explicit auditing. The real requirement is consistency.

Q3. How do I reduce IDOR risk when using AI to code?

Force a gate: every feature PR must include an authorization check and a negative test case. AI is fine at implementation, but humans must own boundaries.

Disclaimer

This article provides general security information and does not guarantee security for any specific system.

Popular

  1. 1Permit2 explained (Web3): why approvals changed and how to use it safely (checklist)
  2. 2Read wallet signing screens (Web3): a 30-second checklist to avoid permission traps
  3. 3Spec-to-implementation prompt template (AI development): how to stop the model from guessing
  4. 4Revoke token approvals on EVM: how to audit allowances safely (checklist)
  5. 5Clarifying questions checklist (AI development): what to ask before you let an LLM build

Related Articles