RedRouterRedRouter

Blog

AI IDOR testing with two owned accounts

Surgical instruments on green cloth, with sparse red pixels and the article title
Cover based on a photo by Saltanat ebli, CC0 1.0

For AI-assisted IDOR testing, give the model an access matrix and redacted request/response evidence from two owned accounts. Ask it to compare the expected authorization rule with observed access. Don't ask it to guess identifiers until it finds someone else's data.

Suppose your local invoice lab has accounts A and B in separate tenants. Each owns one synthetic invoice. B receiving A's private invoice would cross a boundary; B receiving an ordinary login page would not. The distinction lives in the body, session state and ownership rules, not in the model's confidence.

On a real bug bounty program, create accounts and test cross-account behavior only where the policy permits it. Keep all objects synthetic and researcher-controlled. Don't enumerate unrelated records, mutate another user's data or treat this guide as permission to test an asset.

AI IDOR testing starts with the authorization rule

PortSwigger's IDOR guide defines insecure direct object references as access-control vulnerabilities involving user-controlled object references. It describes both horizontal and vertical privilege boundaries. A predictable identifier by itself isn't the vulnerability.

Write down the intended rule before sending a comparison to a model. “Private invoices belong to one tenant; users from another tenant must not read them” is testable. “The URL looks insecure” isn't.

If the product intentionally shares invoices through public links, that changes the expected rule. If account B is also a member of tenant A, that changes it too. Treat unclear ownership as missing evidence, not an invitation to label everything a bypass.

Our authorized security use cases set the same boundary: explicit permission first, model output second.

create two accounts and known objects

In a local training app, create A in tenant T1 and B in tenant T2. Give each a fresh browser profile or separate cookie jar. Create one invoice per account with a recognizable marker. For example, LAB-A-ONLY and LAB-B-ONLY should appear in the respective bodies.

Save creation evidence showing which actor owns which object. Also record sharing settings, tenant memberships and the expected visibility. Don't send raw session cookies to a hosted model. Label them as account A or B in a sanitized export.

A bare object ID is not enough to establish ownership. The control is the account and tenant relationship, not the shape of the identifier. Use only objects whose origin you can explain.

build an actor × object × action matrix

This CSV is a fictional fixture, not a result from RedRouter or a live bounty:

evidence_id,actor,tenant,object,owner_tenant,action,expected,observed,body_marker
E01,A,T1,invoice-A,T1,read,allow,200,LAB-A-ONLY
E02,B,T2,invoice-B,T2,read,allow,200,LAB-B-ONLY
E03,B,T2,invoice-A,T1,read,deny,200,LAB-A-ONLY
E04,A,T1,invoice-B,T2,read,deny,403,NONE
E05,B-fresh,T2,invoice-A,T1,read,deny,200,LAB-A-ONLY

E03 is a candidate crossing. E05 is a fresh-session repetition of the same narrowly bounded check. E04 shows the reverse direction held in this fictional case; it doesn't prove that every endpoint or account is safe.

The example uses read operations because they can demonstrate a boundary without changing the object. Even reads can expose sensitive data or trigger side effects. If a real check returns someone else's data, stop and follow the program's handling rules rather than collecting more examples.

ask the model to compare evidence, not expand the hunt

Compare the supplied CSV and redacted fixtures only. For each unexpected cell, return evidence_ids, expected_rule, observed_behavior, alternative_explanation, missing_evidence and next_manual_check. Use unknown for missing ownership or session facts. A 200 without the expected object marker is insufficient. Do not enumerate IDs, invent requests or execute tools.

Read the proposed explanation against the raw evidence yourself. The response might echo the requested ID but omit private fields. It might be an error envelope wrapped in HTTP 200. It might be an intentionally shared object. Those cases need different conclusions.

If you want machine-readable output, validate it locally. The structured-output documentation explains why a requested schema isn't a factual guarantee. Evidence references should be checked against an allowlist of IDs, not accepted because they look well formatted.

confirm a single boundary safely

The minimum useful proof establishes the actor, the object owner, the intended denial and the actual protected content. Save one owner baseline and one permitted non-owner comparison. Confirm that the model-facing redaction has not removed the very field needed to distinguish tenants.

Check session state immediately before the comparison. Log out and back in through the application's normal flow when appropriate. Never swap credentials into the model prompt to help it “understand” the session.

Inspect cache behavior without broadening the test. A stale response from your own earlier session can mimic a cross-account read. Document cache headers and browser state, and use the least intrusive allowed control. Don't keep probing if a clean control requires actions outside the agreed scope.

false positives worth checking before reporting

Ask the assistant to explain which control distinguishes each alternative. Don't let it dismiss alternatives without evidence.

YesWeHack's July 17, 2026 lab account is useful here for its insistence on manual validation after discovery. Its outcomes are limited to the labs described; they don't prove a particular model will catch your session error.

this is not automatically authentication bypass

A signed-in B reading A's object is an authorization problem. It doesn't establish that a signed-out user can authenticate as A, steal a session or take over an account. Don't convert a horizontal access-control finding into account takeover because the model finds that title more dramatic.

A stronger impact claim needs its own evidence and permission. If the demonstrated issue is one cross-tenant read of a synthetic invoice, report that exact boundary and state what wasn't tested. Severity remains a reasoned assessment under the program's rubric, not a model-generated certainty.

turn the matrix into a minimal report

Use a title that states actor, action and boundary: “Tenant T2 account can read T1's private invoice by object reference.” Include the asset, scope reference, test-account roles, creation steps, owner baseline, non-owner request and the two matching object markers. Explain expected denial versus observed content, then list limitations.

HackerOne's Quality Reports guidance, February 5, 2025, supports that concise reproduction-first structure. A human reviews and submits the report; the assistant can help organize it but should not submit automatically.

For a hosted analysis worker, use an explicit model from the catalog and follow the scoped-worker architecture. Zero data retention is opt-in, off by default and free on monthly plans; retention controls don't override a program's external-processing restrictions.

What we still don't know: the fictional matrix doesn't establish any real vulnerability, model success rate or platform acceptance. It demonstrates the evidence you would need, not a finding you can submit.

Sources checked November 5, 2026 (PT). No live target or model API tested.