IDOR in AI-built web apps (authorization): how to catch it before you ship
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
- Conclusion
- Explanation
- Practical Guide
- Step 1: identify your object boundaries (what must be protected)
- Step 2: inventory every id entry point (fast audit)
- Step 3: enforce one authorization pattern everywhere
- Step 4: pick deny semantics (403 or 404) and be consistent
- Step 5: add the IDOR regression test (the highest ROI)
- Step 6: add logging without leaking PII
- Pitfalls
- Checklist
- FAQ
- Q1. Are UUIDs enough to prevent IDOR?
- Q2. Should I return 403 or 404 on unauthorized access?
- Q3. How do I reduce IDOR risk when using AI to code?
- Internal links
- Disclaimer
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:
- enforce authorization server-side for every resource fetch/mutation
- standardize deny behavior (403 vs 404) and logging
- 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:
/:idroutes- 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:
- authenticate
- fetch by id
- authorize against the fetched object
- 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/userIdsent 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
idis inventoried - [ ] Auth pattern is consistent (authenticate → fetch → authorize → respond)
- [ ] Client-provided
orgId/userIdis 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.
Internal links
- Parent hub: AI development: start here
- Related:
Disclaimer
This article provides general security information and does not guarantee security for any specific system.
Popular
- 1Permit2 explained (Web3): why approvals changed and how to use it safely (checklist)
- 2Read wallet signing screens (Web3): a 30-second checklist to avoid permission traps
- 3Spec-to-implementation prompt template (AI development): how to stop the model from guessing
- 4Revoke token approvals on EVM: how to audit allowances safely (checklist)
- 5Clarifying questions checklist (AI development): what to ask before you let an LLM build