On this page
How to Test Your App for Broken Access Control
A practical guide to testing for IDOR and privilege escalation: two accounts, cross-account ID swaps, and role checks. No security background required.
Quick answer
- Test access control with two accounts: create data as user A, then try to read or change it as user B.
- The telltale sign is a request that works when it should have been denied.
- Cover both horizontal access (other users’ data) and vertical access (other roles’ actions).
What you’re testing
Authorization — whether users can only do what they’re allowed to do — as opposed to authentication, which only proves who they are. Broken access control is the top OWASP category, and it’s invisible until you actively try to cross a boundary.
Step 1 — Set up two accounts
Create two ordinary user accounts (A and B) and, if you have roles, an admin account. You’ll use them to test from each side of the trust boundary.
How to verify it worked: each account can log in and reach its own data.
Step 2 — Test horizontal access (IDOR)
As user A, create a record and note its ID. Log in as user B and request the same ID directly — by URL, by API call, or by editing the request:
# as user B, try to read A's record
curl -H "Authorization: Bearer $B_TOKEN" https://api.example.com/invoices/1234How to verify it worked: the request must return 403 or 404 — not A’s data. If it returns the data, you found an IDOR.
Step 3 — Test vertical access (privilege escalation)
Log in as user B and attempt an admin-only action — creating a user, exporting all records, or calling an admin endpoint. Try both the UI and the raw API, since UI hiding is not security.
How to verify it worked: the action is denied server-side. A hidden button that still works when called directly is a real vulnerability.
Step 4 — Test across every endpoint
Repeat the pattern on each resource type: invoices, profiles, files, admin routes, and bulk operations. The most common finding is a few guarded endpoints next to several unguarded ones — the inconsistency that AI-generated code is prone to.
Step 5 — Automate what you can
Encode the checks as tests that run in CI: a test that asserts user B receives 403 for user A’s resource. This keeps the boundary enforced as the code changes.
Where this bites vibecoders
An AI assistant generates endpoints uniformly, so access-control gaps appear uniformly too — a whole resource type with no ownership checks. The two-account test is cheap, requires no security tools, and catches the exact bug the assistant will not catch for you. Run it before you show the app to anyone.
Where AI coding assistants get this wrong
- Generating UI that hides admin controls without any server-side check.
- Adding authorization to some endpoints and silently skipping others.
- Confusing authentication (“logged in”) with authorization (“allowed to see this”).
Checklist
- Create separate accounts for testing, including role variants.
- Attempt cross-account reads and writes on every resource type.
- Attempt higher-role actions from a lower-role account.
- Confirm denial happens server-side, not just in the UI.
- Turn the key checks into automated tests.
FAQ
What is the difference between horizontal and vertical access control?
Horizontal access control is between equal users — can user B read user A’s data? Vertical access control is between roles — can a regular user perform an admin action? Both must be tested; they fail in different ways.
Should a denied request return 403 or 404?
Either is acceptable as long as it denies access. Returning 404 for objects you don’t own is common because it also hides the object’s existence. The failure is returning the data, not the specific status code.
Is this the same as penetration testing?
It’s one slice of it. Penetration testing covers many vulnerability classes; access-control testing is a focused, high-value subset you can do yourself. See What Is Penetration Testing?.