Broken Access Control Testing: How to Find OWASP A01 Vulnerabilities
Broken access control has been the number one web application vulnerability in OWASP's Top 10 since 2021. It dethroned injection after two decades at the top. That ranking reflects something real: it's extremely common, often subtle, and the kind of flaw that causes the highest-profile data breaches.
Unlike SQL injection or XSS, broken access control doesn't show up as a syntax error or a script tag in a response. It shows up as "I can see your data," "I can take your actions," or "I accessed a page I wasn't supposed to reach." That makes it harder to test automatically and much more dependent on understanding the application's intended authorization model.
This guide covers how to test for broken access control systematically — what to look for, how to probe it manually, what automated tools catch, and what they reliably miss.
What Broken Access Control Actually Covers
OWASP A01 is a broad category. It includes several distinct patterns that behave differently and require different testing approaches:
?user_id=123 to ?user_id=124 to access another account's data. The most common form and the easiest to miss with automated tools if they can't reason about data ownership.
../../../etc/passwd style attacks, or relative path manipulation in file download endpoints.
role: "user" claim to role: "admin", or altering a sub claim to access another user's data. Often combined with algorithm confusion attacks (alg: "none" or RS256 → HS256 downgrade).
Testing Methodology
Step 1: Map the authorization model
Before you test anything, understand what access control is supposed to do. For each user role in the application, document:
- What resources each role can read, write, and delete
- What actions each role can take (approve, export, invite, configure)
- Whether the application has multi-tenancy (data segregation between organizations)
- Which endpoints are admin-only vs. user-accessible
This map becomes your oracle for what should and shouldn't be accessible. Testing without it means you're guessing, not verifying.
Step 2: Create test accounts at each privilege level
You need at least:
- Two accounts at the same privilege level (for horizontal escalation testing)
- One account at each privilege level that exists in the application
- One account with no privileges / unauthenticated session
If the application has organizations or tenants, create accounts in at least two separate tenants.
Step 3: Capture the authenticated request flow
Use a proxy (Burp Suite, OWASP ZAP, or any intercepting proxy) to capture authenticated requests from Account A. For each request that involves a resource identifier — user_id, document_id, order_id, account_id — note the identifier value.
Then replay those requests with Account B's session token, substituting Account A's resource identifiers. If Account B can read, modify, or delete Account A's data: IDOR confirmed.
Step 4: Test vertical escalation on every privilege-sensitive endpoint
For each endpoint that should be admin-only, replay the request using a standard user's session. For each endpoint that requires authentication, replay without authentication. Watch for:
- 200 OK (full access granted — direct finding)
- 200 with partial data (access control partially enforced)
- 302 redirect to login page only for unauthenticated (not for low-priv users — common gap)
- Different response bodies vs. the admin version (sometimes the data is there but filtered)
Step 5: Enumerate hidden endpoints
Developers often build admin panels, export endpoints, and internal APIs that are never linked from the UI. Common patterns to probe:
/admin/
/admin/users
/admin/settings
/api/admin/
/api/v1/admin/
/internal/
/management/
/debug/
/console/
Use wordlists (SecLists Discovery/Web-Content/api/api-endpoints.txt and Discovery/Web-Content/common.txt) with a fuzzer against the authenticated session. A 200 or 403 (rather than 404) signals the endpoint exists.
Step 6: Test parameter manipulation
Beyond direct object references, look for:
- Role parameters in requests —
role=userin a profile update that you can change torole=admin - Boolean flags —
is_admin=false,premium=false,verified=falsethat you can flip - Price and quantity manipulation — changing the price of an item in the request (not just display-side)
- Hidden form fields — form fields that aren't shown in the UI but are submitted and processed server-side
JWT-Specific Testing
If the application uses JSON Web Tokens for authentication or authorization, these deserve dedicated attention.
Algorithm confusion (alg: none)
Some JWT libraries accept tokens with alg: "none" — no signature required. Decode your JWT, modify the payload (role: "admin"), set alg: "none", and remove the signature. If the server accepts it, the application is vulnerable.
# Decode the token (base64 each section)
echo "eyJhbGciOiJIUzI1NiJ9" | base64 -d
# {"alg":"HS256"}
# Craft a new header with alg: none
echo -n '{"alg":"none"}' | base64 | tr -d '='
# eyJhbGciOiJub25lIn0
# Build the token: header.payload. (no signature)
# Submit as: Authorization: Bearer eyJhbGciOiJub25lIn0.{modified_payload}.
RS256 to HS256 confusion
When a server uses RS256 (asymmetric), the public key is often available. Some libraries, when switched to HS256, will verify the signature using the public key as the HMAC secret. Forge a token signed with HS256 using the server's public key as the secret.
Claim manipulation
If the JWT is signed but not encrypted, you can read the claims. Check what claims are present and whether modifying them (after forging a new signature) changes authorization behavior. Common high-value claims: role, sub, user_id, org_id, tenant_id, permissions.
What Automated Scanners Catch (and What They Miss)
Access control testing is the hardest category for automated tools. Here's what to expect:
| Pattern | Automated detection | Why |
|---|---|---|
| Path traversal | Good | Predictable payload patterns; response differences are detectable |
| IDOR on sequential IDs | Moderate | Scanner can increment IDs, but needs two accounts to compare responses |
| Missing function-level control | Moderate | Endpoint discovery works; verifying access requires understanding role model |
| Vertical privilege escalation | Limited | Requires understanding what admin vs. user should see — application-specific |
| IDOR on UUIDs / non-sequential IDs | Poor | Can't enumerate UUIDs by incrementing; requires known-good values from another account |
| Business logic access control | Very limited | Multi-step flows with contextual authorization can't be modeled by generic scanners |
| JWT algorithm confusion | Good | Well-known attack patterns; scanners can probe alg: none systematically |
The honest summary: automated tools do well on the mechanical patterns (path traversal, JWT attacks, endpoint enumeration). They struggle with anything that requires reasoning about data ownership or the intended authorization model — which is exactly where the most impactful vulnerabilities hide.
IDOR in APIs vs. Traditional Web Apps
REST APIs surface IDOR more prominently because identifiers are usually explicit in URLs and request bodies rather than hidden in state. Watch for:
GET /api/v1/users/{user_id}— can you retrieve another user's profile?GET /api/v1/orders/{order_id}— can you read someone else's order?PUT /api/v1/accounts/{account_id}/settings— can you modify another account's settings?DELETE /api/v1/documents/{doc_id}— can you delete documents you don't own?
GraphQL deserves special attention. Object-level authorization is harder to enforce uniformly across a graph, and developers sometimes apply authorization at the resolver level rather than at the field level. Test every node and field type that returns user-scoped data.
Multi-Tenancy Testing
SaaS applications with multi-tenant architectures have an additional access control dimension: tenant isolation. A user in Organization A should never be able to read or modify data belonging to Organization B.
Test this systematically:
- Create accounts in two separate tenants
- Create resources (documents, records, configurations) in Tenant A
- Capture the identifiers of those resources
- Authenticate as a Tenant B user and attempt to access those identifiers
- Test both read access and write/delete access
Also test whether Tenant A's admin can access Tenant B's data. Tenant admin ≠ application admin, and this boundary is sometimes implemented inconsistently.
Access Control Testing Checklist
- Map the full authorization model before testing (roles, resources, actions)
- Create test accounts at every privilege level and in multiple tenants
- Test IDOR: replay requests with Account A's identifiers using Account B's session
- Test vertical escalation: replay admin-only requests with a standard user session
- Test unauthenticated access: replay all authenticated endpoints without a session token
- Enumerate hidden endpoints with wordlists against an authenticated session
- Check HTTP method overrides: some servers accept
X-HTTP-Method-Override: DELETEeven when the endpoint rejects DELETE directly - Test parameter manipulation: role flags, boolean parameters, hidden form fields
- Test JWT tokens: algorithm confusion, claim manipulation, expiry bypass
- Test tenant isolation: cross-tenant resource access with both read and write operations
- Test GraphQL: per-field authorization, object-level access across all types
What Good Remediation Looks Like
Broken access control is an architectural problem as much as an implementation problem. Fixing individual IDOR findings without addressing the underlying pattern means you'll find the same class of bug in the next endpoint you add.
The durable fix is a centralized authorization layer:
- Policy-based authorization — define access rules in one place (role definitions, ownership checks) rather than scattering
if user.is_adminchecks across endpoint handlers - Object-level authorization as a primitive — make "does this user own this resource?" a first-class function that every data access goes through, not an afterthought
- Default-deny — new endpoints should require explicit authorization grants, not be open until someone adds a check
- Deny on missing claims — if a JWT is missing an expected claim, deny the request rather than treating the absence as a falsy value
For JWT-specific fixes: always verify the algorithm explicitly (never accept alg: none), use RS256 with proper key rotation, and keep sensitive authorization data server-side rather than embedding it in tokens that clients can manipulate.
Ironimo tests for broken access control patterns across your entire application — path traversal, JWT algorithm confusion, endpoint enumeration, and IDOR on sequential identifiers — using the same Kali Linux toolset professional pentesters run against production applications.
Schedule on-demand or recurring scans. Results include the exact request that reproduced the finding, the response that confirms it, and a remediation path.
Start free scan