Quick answer
IDOR and BAC are still #1 on OWASP API Security in 2026. The rule: automated scanners don't find them — they require business context (what does this endpoint do?) and two accounts to verify the access boundary. The methodology: two accounts (A and B), capture A's requests in Burp, swap the IDs/cookies for B's, observe the response. If A can view/edit/delete B's resources, it's IDOR. If a normal user can reach admin endpoints, it's vertical BAC. Typical bounties: €500-€10,000+ depending on impact.
Setup — two accounts in parallel
The beginner's mistake: testing IDOR with a single account. Without account B you have no baseline — you don't know whether the server rejects A's user or whether the ID simply doesn't exist.
Stack:
- Burp Suite (Community is fine) with two browsers (Chrome profile A, Firefox profile B).
- Or two profiles of the same browser + extensions like Auth Analyzer or Autorize.
- Match-and-replace in Burp to swap cookies automatically.
Browser A (Chrome) → Burp Proxy → target.com
Browser B (Firefox) → Burp Proxy → target.com
Capture a request from A, send it to Repeater, change only the auth cookie/header for B's (everything else the same). If the response is the same as A's → IDOR confirmed.
[!tip] Auth Analyzer extension It authorizes Burp to re-send each request with a different user's session and compare the response. The fastest way to audit 50+ endpoints without a script.
The 8 surfaces where IDOR appears
| # | Surface | Example URL | Test |
|---|---|---|---|
| 1 | Numeric path parameter | /api/users/12345 | 12344, 12346, 0, -1 |
| 2 | UUID path parameter | /api/orders/abc-def-ghi | B's UUID obtained another way |
| 3 | Query parameter | /profile?userId=42 | Change to 43 |
| 4 | Body JSON field | {"userId": 42, "data": "..."} | Swap userId to B's |
| 5 | Custom header | X-User-Id: 42 | Change to 43 |
| 6 | Cookie value | Cookie: uid=42; ... | Change to 43 |
| 7 | Hidden form field | <input type="hidden" name="user_id" value="42"> | Change before submit |
| 8 | JWT claim | {"sub": "42", "role": "user"} | Re-sign with sub=43 if the signature is weak |
The golden trick: many apps validate the ID in the path but not the one in the body. If the endpoint is POST /api/users/42/profile with body {"userId": 42, "newName": "..."}, try leaving the path at 42 but putting userId: 43 in the body. Frequently the server ignores the path and trusts the body.
How to obtain other users' IDs
Without B's IDs (or other users') you can't confirm IDOR. Sources:
- Your own account B: register it with a different email and observe which IDs appear.
- Public listing endpoints:
/users/search,/api/leaderboard,/comments?postId=X. - Features that leak IDs: avatars (
/avatars/USER_ID.jpg), notifications (from_user_id), public posts. - Shared URLs: invitation, magic link, purchase receipt that includes an order ID.
- Source maps + JS bundles: sometimes the frontend routers list hardcoded IDs from demos.
- GraphQL introspection + search queries without a visibility filter.
[!warning] Privacy + scope If you find an IDOR that leaks PII (email, phone, address, national ID), document it WITH your own control account — do not run bulk extraction against real users. Most programs forbid extracting >1-2 records from non-owned accounts.
Horizontal vs vertical IDOR
Horizontal — same role, different identity. User A accesses user B's resources:
GET /api/orders/B_ORDER_ID
Authorization: Bearer JWT_DE_A
← 200 OK con datos de B = IDOR horizontal
Vertical — privilege escalation. A normal user accesses admin endpoints/functions:
DELETE /api/admin/users/12
Authorization: Bearer JWT_DE_USER_NORMAL
← 200 OK = BAC vertical
Vertical weighs more in CVSS — usually Critical (9.0+). Typical bounties €5K-€20K+.
Parameter swap — the most profitable pattern
Almost all profitable IDORs come from swapping a parameter in an endpoint that looks harmless:
Email change — without verification
PUT /api/users/me/email
{"email": "atacante@evil.tld", "userId": ID_DE_VICTIMA}
The server updates the email of the body's userId, ignores the me in the path → password reset via a controlled email → ATO.
Password change — oldPassword not verified
POST /api/users/CHANGE_PASSWORD
{"userId": ID_VICTIMA, "newPassword": "atacante"}
The endpoint doesn't ask for oldPassword. Typically a €3K-€8K bounty.
Order cancellation — refund to the attacker's account
POST /api/orders/B_ORDER_ID/refund
{"refundTo": "atacante_bank_account"}
Role assignment on a team
POST /api/teams/T_ID/members
{"userId": ATACANTE_ID, "role": "owner"}
If the endpoint doesn't verify that the actor is the team's owner → any user can join any team as owner.
Hidden parameters — the ones that generate big bounties
Many endpoints accept parameters that don't appear in legitimate UI requests. The backend processes them if they're present, but the frontend never sends them. Result: undocumented features, sometimes admin ones.
Wordlist of frequent parameters
admin, isAdmin, is_admin, role, userRole, accountType
debug, test, dev, sandbox, preview
force, bypass, override, skipAuth
verified, isVerified, emailVerified
status, accountStatus, suspended, banned
internal, system, super, root
hidden, public, visible
Tools
# Arjun — descubre parámetros HTTP ocultos
arjun -u https://target.com/api/users -m GET
# Param Miner (Burp extension)
# Right-click request → Extensions → Param Miner → Guess params
Examples of real bounties with hidden params
POST /api/userswith a normal body{email, password}+ extra{"role": "admin"}→ new user created as admin.PUT /api/posts/Xwith extra{"approvedBy": "system"}→ bypasses the moderation queue.GET /api/orders/X?debug=true→ expanded response with internal fields (PII, internal IDs, cost).
Function-level BAC — endpoints the frontend never calls
The normal UI never calls /api/admin/users from a normal user — but the endpoint still exists and sometimes doesn't check the role.
How to find them
- Log in as admin (a test account if the program allows, or a normal user with high privileges).
- Capture all the endpoints the admin dashboard calls.
- Log out, log in as a normal user.
- Re-fire the same endpoints. Is there one that responds 200?
You frequently find endpoints that verify the token (auth) but not the role (authorization).
admin → GET /api/admin/users → 200 [lista de users]
user → GET /api/admin/users → 200 [lista de users] ← BAC vertical
Method swap
Some endpoints only validate the correct HTTP method. Try:
POST /api/admin/users → 403 Forbidden
GET /api/admin/users → 200 OK ← BAC
PUT /api/admin/users/42 → 403 Forbidden
PATCH /api/admin/users/42 → 200 OK ← BAC
Trailing slash + extension trick
Some WAFs filter exact paths. Try variants:
/api/admin/users → 403
/api/admin/users/ → 200 ← trailing slash bypass
/api/admin/users.json → 200 ← extension
/api/admin/users;.css → 200 ← matrix params
/api/admin/users%20 → 200 ← trailing whitespace
/api/Admin/users → 200 ← case
/api/admin//users → 200 ← double slash
IDOR in GraphQL
GraphQL multiplies the surface: a single endpoint exposes N queries. Access control usually lives at the resolver level, not the field level — it's very easy to miss one.
Test pattern
query {
user(id: "OTRO_USER_ID") {
email
phoneNumber
addresses { street }
paymentMethods { last4 }
}
}
If the app doesn't check ownership in the user resolver, it returns it. Repeat with node(id: ...) (a common interface in relay) and with search queries without a visibility filter.
Mutations
mutation {
updateUser(id: "VICTIMA_ID", input: { email: "atacante@evil.tld" }) {
id
}
}
Many GraphQL backends verify that the user is authenticated but not that they own the id they're asking to modify.
Reporting tips
- Severity boost: if the IDOR leaks regulated PII (national ID, medical, financial data) → privacy impact, GDPR.
- Account takeover chain: if IDOR allows changing the email → immediate ATO, severity Critical.
- Bulk extraction warning: document the pattern with 2-3 records from your account + a control account. Do NOT extract thousands.
- Bypass docs: list every filter the server attempted (path validation, body validation, method check) and how you bypassed it — it raises the reward and leads to a more solid dev fix.
Hunting checklist
- Set up two accounts (A and B) in parallel with Burp/Auth Analyzer.
- Map every endpoint that accepts an ID in path, query, body, header or cookie.
- Swap the ID with B's session, observe the response.
- Try all actions (GET / PUT / DELETE / POST) — some validate GET but not DELETE.
- Hidden parameters with Arjun + Param Miner.
- Log in as admin (if you can), capture admin endpoints, reproduce with a normal user.
- Method swap + trailing slash + extension tricks on 403 endpoints.
- GraphQL: try
user(id),node(id), listing queries without a filter. - Cross-tenant: if the app is multi-tenant, try cross-org accesses.
- Document with 2-3 records max, never bulk extract.
Related labs
Practice horizontal/vertical IDOR, hidden params and function-level BAC in IDOR and BAC labs.
Practice this in a lab
Idor Api Newsletter Suscriptores
Keep learning · free account
Save your progress, unlock advanced payloads and rank your flags.
There's an extra payload at the end
Los 12 parámetros ocultos que tests automáticos no detectan pero generan bounties de €1500-€5000 con regularidad.
€7.99/mo · cancel anytime
Related articles
Client-side admin bypass — boolean manipulation + BAC in a modern SPA
Real Quora report: SPA with an isAdmin boolean in localStorage that controls the UI + a backend that doesn't validate server-side. How to chain a boolean flip with BAC for admin takeover.
IDOR in a newsletter API — the UI hides it, the API hands it over
An endpoint with no server-side authorization check. A public parameter in the URL. Access to the subscriber list of any newsletter on a professional social network.
Mass PII Extraction via GraphQL — 93 real profiles in 1 hour
A contact-sync GraphQL endpoint with no rate limiting, no ownership verification and batching of 200 numbers per request. Phone → real identity resolution.