← marwandiallo.comlabs

BOLA / IDOR patterns

This route is now a live query-scoping lab. Pick an authenticated user, a target order, and a server-side query pattern. The findings rerun live based on whether the route trusts the URL, fetches then rejects, scopes the query correctly, or relies on a client-controlled admin escape hatch.

Live request preview

Actor: Alice · Resource owner: Bob

GET /api/orders/ord_1003
session.sub = u_alice

SELECT * FROM orders WHERE id = ?

Result: HTTP 200

{
  "id": "ord_1003",
  "ownerId": "u_bob",
  "total": 312,
  "shippingAddress": "9 Oak Ave, Brooklyn NY",
  "paymentLast4": "0007",
  "notes": "Replacement for damaged item."
}
CRITICALLIVE01

Cross-owner read succeeds under by-id lookup

The request authenticates the actor but never scopes the resource query by owner. Alice can read Bob's order just by changing the URL identifier.

fix: Bind resource lookup to the authenticated principal in the same query: WHERE id = ? AND owner_id = ?.

CRITICALBOLA01

Resource fetched by ID without owner scope

The endpoint queries for the resource by primary key and returns whatever it finds. Any authenticated user can change the ID in the URL and read other users' data. This is OWASP API #1 and accounts for the largest share of bug-bounty payouts.

fix: Scope the query by the authenticated principal: WHERE id = ? AND owner_id = ?. Do not 'fetch then check'.

standards: OWASP API Top 10 API1 · CWE-639 · NIST SP 800-53 AC-3 · PCI-DSS 4.0 §7.2.5

HIGHBOLA02

403 Forbidden returned for cross-owner access

Returning 403 confirms the resource exists; an attacker can enumerate IDs to map other users' inventory. Real outcome: confirmed which order IDs are valid before they steal them.

fix: Return 404 Not Found for both 'does not exist' and 'not yours'. Indistinguishable.

standards: OWASP API Top 10 API1 · CWE-204

HIGHBOLA03

Sequential / guessable resource IDs

Auto-incrementing IDs make BOLA discovery trivial — change ord_1001 to ord_1002 and try again. UUIDs don't fix the underlying authorization bug but raise the cost of mass enumeration significantly.

fix: Use UUIDv4 or another opaque identifier. Combine with the BOLA01 fix; UUIDs alone are not authorization.

standards: OWASP API Top 10 API1 · CWE-340

HIGHBOLA04

Authorization in middleware only, not in the query

Middleware checks 'is the user logged in', then the route fetches by ID. This separation of concerns is correct for AuthN, dangerous for AuthZ — the route still needs to verify ownership.

fix: Push owner-scope into the data layer: a repository method that takes (id, principal) and refuses to return cross-tenant rows.

standards: OWASP API Top 10 API1 · CWE-285 · NIST SP 800-53 AC-3

HIGHBOLA05

Mass-assignment on update endpoint

An update endpoint accepts the full order object including ownerId, allowing an attacker to reassign their own order to another user, or to escalate via role= fields.

fix: Whitelist updatable fields. Never trust client-supplied id/owner/role/createdBy on writes.

standards: OWASP API Top 10 API3 · CWE-915

MEDIUMLIVE04

Guessable identifiers amplify enumeration

The core bug is authorization, but short sequential IDs make discovery faster and make 403/404 side channels far more useful.

fix: Use opaque identifiers and still fix owner scoping in the query. Opaque IDs are friction, not authorization.

MEDIUMBOLA06

Admin override flag in client request

Endpoint accepts ?admin=true or X-Admin: 1 and skips the owner check. Frequently used by internal tools and never removed before launch.

fix: Trust only server-side claims (session, JWT validated server-side). Strip request-level admin flags at the edge.

standards: OWASP API Top 10 API5 · CWE-285

MEDIUMBOLA07

GraphQL by-ID query without owner scope

GraphQL resolvers commonly expose order(id: ID!) that resolves any record. Same bug as REST; harder to spot because of the schema layer.

fix: Pass the principal into the resolver context. Apply ownership in the data loader, not the resolver. Consider Relay-style global IDs that encode owner.

standards: OWASP API Top 10 API1 · CWE-639

LOWBOLA08

Verbose error messages leak resource shape

Errors like 'order not found in tenant t_acme' confirm tenant existence and naming. Useful for an attacker building a cross-tenant attack chain.

fix: Return generic errors to clients. Log the detail server-side with a correlation ID.

standards: OWASP Top 10 A04 · CWE-209

The two questions to ask any list-or-fetch endpoint

  1. Whose data does this query return? If the answer isn't "the authenticated principal's", the next question matters.
  2. Where is ownership enforced? "In the route handler" is a yellow flag. "In the data layer, in the same query that does the fetch" is what you want.