Articles
IDOR Interview Questions: Object-Level Authorization and Test Design
Prepare for IDOR interviews with object-level authorization, tenant and owner test matrices, bulk endpoints, revocation, and denied side-effect checks.
IDOR interview questions test whether you can connect a request to the specific object the caller is allowed to use. A valid session proves who sent the request. It does not prove that the caller may read this invoice, change this address, or download this export. The important check combines actor, action, object, and the application's permission rules.
A strong answer also explains how you would prove that check works. This article uses a fictional invoice service to make those decisions observable.
What makes an object reference insecure?
Official facts: OWASP describes IDOR as missing object-level access control around identifiers supplied through requests. Identifiers can appear in paths, bodies, filenames, or other parameters. Their presence is normal; the vulnerability is allowing an unauthorized operation on the referenced object. OWASP IDOR Prevention
Suppose an authenticated employee requests invoice I7. The service loads I7 and returns it because the employee has the general invoice_reader role. That check is incomplete if the role permits reading only invoices in the employee's organization. The endpoint exists, the session is valid, and the object is real, but the relationship is wrong.
Editorial inference: all fixtures, code sketches, and review exercises here are original preparation material for systems you are authorized to test.
Do not reduce the explanation to changing a number in a URL. That example can hide missing checks in bulk operations, nested resources, and asynchronous downloads. Start with the permission that should hold, then identify every request path capable of violating it.
Why UUIDs do not replace authorization
An unpredictable identifier makes guessing harder, but identifiers can be shared through legitimate workflows, browser history, logs, notifications, or another endpoint. If knowing an ordinary invoice ID is enough to read it, the service has confused identification with permission.
Official guidance: OWASP treats complex identifiers as defense in depth and still requires access checks. It also recommends looking up objects within the data the current user may access. OWASP IDOR Prevention
There is a useful exception to discuss carefully: a deliberately designed bearer capability may grant access through possession of an unguessable secret URL. That is an explicit authorization mechanism with its own scope, lifetime, storage, and revocation rules. It is not a reason to assume every database UUID is a capability.
In the invoice exercise, IDs are identifiers only. Even if I7 were a long random value, its holder would still need the relevant organization membership and invoice permission. State that assumption before proposing tests so the interviewer can distinguish a policy violation from intended link sharing.
Build a fixture matrix before sending requests
Create synthetic users and invoices with known relationships. Alice and Bob belong to Tenant A; Carol belongs to Tenant B. Alice owns I1, Bob owns I2, and Carol owns I3. A billing administrator belongs to Tenant A. For this exercise, ordinary users may read their own invoices, while the administrator may read every invoice in their tenant.
This is a policy choice, not a universal billing rule. If shared access is introduced, add that relationship explicitly rather than treating ownership as the only possible authorization model.
| Actor | Object | Read | Change billing note | Reason |
|---|---|---|---|---|
| Alice | I1, owned by Alice | Allow | Allow | Owner permission |
| Alice | I2, owned by Bob | Deny | Deny | Same tenant does not imply ownership |
| Alice | I3, Tenant B | Deny | Deny | Cross-tenant boundary |
| Tenant A billing administrator | I2 | Allow | Allow | Authorized tenant role |
| Tenant A billing administrator | I3 | Deny | Deny | Role is not global |
| Signed-out caller | I1 | Deny | Deny | No authenticated principal |
Official testing guidance: OWASP WSTG recommends multiple users with different objects and privileges so testers can use known references instead of guessing. WSTG authorization testing
The matrix is your expected-result oracle. Without it, a successful response may be either a vulnerability or intended access. Ask about delegated access, support roles, and lifecycle state before labeling the result.
Where should the permission check live?
A useful server-side sequence is: authenticate the request, derive trusted principal context, resolve the permitted object scope, check the requested action, and perform the operation. Tenant membership should come from verified server context, not merely a caller-supplied tenant_id field.
Here is an original pseudocode sketch for the ordinary-owner path:
principal = authenticated_principal(request)
invoice = invoices.find_one(
id=request.invoice_id,
tenant_id=principal.tenant_id,
owner_id=principal.user_id
)
if invoice is absent:
return not_found_under_policy
check_invoice_action(principal, "update_note", invoice)
update_note(invoice, validated_note)
The administrator branch needs its own verified tenant-role policy; do not copy the owner filter and accidentally block intended access. Equally, do not remove the owner filter globally just to make one administrator test pass.
Official guidance: OWASP recommends denying by default and validating permissions on every request. Its authorization guidance also distinguishes role-based checks from relationship and attribute-based decisions. OWASP Authorization Cheat Sheet
Centralizing policy reduces drift, but “we use middleware” is not sufficient evidence. Middleware may know the user and route without knowing which invoice the request ultimately accesses. Identify where the object and action become available, and ensure every entry point reaches the relevant decision.
Test the object, the action, and the side effect
For each allowed matrix cell, first verify the legitimate action succeeds. Then change only the actor-object relationship and expect denial. Keeping unrelated inputs stable makes the failure easier to attribute to authorization rather than malformed data or a broken fixture.
For a denied read, assert that protected fields and download links are absent. For a denied update, reread the invoice using an authorized account and confirm the note did not change. For a denied export, confirm that no downloadable job artifact was created. A service can return an error after performing the forbidden write.
The exact response may be 403 or a deliberately indistinguishable 404, depending on the product's disclosure policy. Do not declare a system safe because one status code appeared. Check the response body, persistent state, and any relevant asynchronous effect.
An original regression case is: Alice attempts to change I2's note to a unique synthetic marker. The test expects denial, then Bob reads I2 and verifies its original note. That second read is the crucial oracle. A test that checks only Alice's response could miss a write followed by an exception.
Nested and bulk endpoints need their own cases
Consider an endpoint shaped like /projects/P1/invoices/I2. It must verify both the caller's access and the relationship between I2 and P1. Checking access to P1, then loading I2 globally, can cross a boundary even when both individual identifiers are syntactically valid.
Now consider a bulk export containing I1 and I3. Define whether the endpoint rejects the whole request or returns only authorized objects. Either contract can be intentional, but it must not leak I3 through records, counts, filenames, or an export artifact. Silent partial success also needs clear client semantics.
Official facts: OWASP API1:2023 describes broken object-level authorization around endpoints that receive object identifiers and act on those objects. General endpoint access does not establish access to each requested object. OWASP API Security Top 10
Add tests for references in JSON arrays, query parameters, and indirect lookup keys. Do not assume the single-item handler's protection also covers a bulk query written separately. Trace the actual resolver or repository method used by each route.
For asynchronous exports, decide when authorization is evaluated: submission, execution, retrieval, or more than one stage. If access changes while a job waits, the policy must specify whether the previously requested data remains available. Then test that chosen behavior.
What changes when permissions are revoked?
An authorization cache can preserve an outdated allow decision. The useful interview question is not simply whether caching is allowed, but which event invalidates the decision and how long stale access may remain possible.
For the exercise, Alice receives temporary access to I2 and later loses it. Verify a new read is denied after the promised revocation boundary. Repeat through the ordinary API, cached response path, and export-download path. If a signed download link remains valid until expiry, disclose that as part of the capability contract.
A write introduces another ordering issue. Access may be checked before a transaction changes ownership or tenant association. If the protected update can race with that change, explain how the system binds the authorization decision to the object version or transactional state used for the write.
This does not mean every permission decision requires one global lock. It means the design must identify the consistency guarantee it actually needs. “Checked earlier” is weak evidence when a later operation can affect a different object or a changed ownership relation.
Separate object authorization from neighboring bugs
An endpoint may correctly authorize access to I1 but allow Alice to change its protected owner_id field. That raises property-level authorization or mass-assignment concerns. It can also create later object-level failures, but naming the first broken boundary helps select the correct fix.
Likewise, a hidden administrator route with no role check concerns access to a function. An SQL injection issue concerns how input changes query meaning. These categories can interact, yet adding an object check does not automatically repair every neighboring flaw.
In your review, describe the operation and violated invariant before the label. “Alice can update Bob's invoice note because the update query uses only the invoice ID” is actionable. “This looks like an OWASP problem” leaves the engineer to reconstruct the mechanism.
For a bounded test report, include the synthetic accounts, intended policy, relevant endpoint, observed result, side-effect evidence, and a regression test. Avoid copying unrelated customer data into the report; a minimal controlled example is stronger evidence and easier to reproduce.