Infinity8 Labs is now Breachvane Labs.

Broken access control & IDOR

IDOR

The application checks who you are but not whether you're allowed to touch a specific record or function, so changing an ID or calling an endpoint directly exposes other users' data or admin actions.

Also known as
Insecure direct object reference · Broken object level authorization (BOLA) · Missing function level access control
Typical severity · Medium to Critical
Last updated
On this page: What it is
  1. 1 · What it is
  2. 2 · Why it matters
  3. 3 · Examples
  4. 4 · How to fix it
  5. 5 · How to test for it
  6. References
  7. Related

What it is#

Access control decides what an authenticated user may do. It breaks when the server trusts the client to only ask for things it's allowed to see.

  • IDOR / BOLA: an endpoint like GET /api/invoices/1043 returns invoice 1043 to any logged-in user, so changing the number returns someone else's invoice.
  • Missing function-level checks: admin endpoints are hidden in the UI but not protected on the server.
  • Privilege escalation through writable fields: a user can set role, tenant_id or is_admin in a profile update (mass assignment).
  • Multi-tenant leaks: queries filter by record ID but not by the caller's organization.

Why it matters#

  • Bulk exposure of other customers' personal or financial data, often by simply counting through IDs.
  • Unauthorized changes: editing or deleting other users' records, approving your own requests.
  • Vertical escalation to admin functions, and in multi-tenant SaaS, crossing from one customer to another.
  • These bugs are easy to exploit, hard to see in logs because requests look normal, and are consistently the top category in the OWASP Top 10.
Typical severity

Medium when exposure is limited or non-sensitive. High to critical when it exposes regulated data at scale, crosses tenants, or grants administrative actions. Actual severity depends on context: who is affected, what data or actions are reachable, and existing controls.

Examples#

✕ Vulnerablejavascript
// Vulnerable: authenticated, but no ownership check
app.get('/api/invoices/:id', requireLogin, async (req, res) => {
  const invoice = await db.invoice.findUnique({ where: { id: req.params.id } });
  res.json(invoice);
});
✓ Fixedjavascript
// Fixed: scope the lookup to the caller's account
app.get('/api/invoices/:id', requireLogin, async (req, res) => {
  const invoice = await db.invoice.findFirst({
    where: { id: req.params.id, accountId: req.user.accountId },
  });
  if (!invoice) return res.sendStatus(404);
  res.json(invoice);
});
✕ Vulnerablepython
# Vulnerable: mass assignment lets a user change their own role
user.update(**request.json)   # {"display_name": "Sam", "role": "admin"}
✓ Fixedpython
# Fixed: explicit allow-list of writable fields
ALLOWED = {"display_name", "timezone"}
user.update(**{k: v for k, v in request.json.items() if k in ALLOWED})

How to fix it#

1. Enforce authorization on the server for every request, scoped to the caller:

✓ See fixed example 1 in Examples ↑

2. Centralize policy. Put authorization in one place (middleware, a policy library such as CASL, Pundit, OPA or Cedar) instead of ad hoc checks in each handler. Deny by default.

✓ See fixed example 2 in Examples ↑

3. Protect functions, not just pages. Every admin or privileged API route must check the role or permission server-side, whether or not the UI shows the button.

4. Make IDs harder to guess. Random UUIDs reduce enumeration but are not a fix on their own; the ownership check is.

5. Log and alert on authorization failures and unusual access patterns, such as one user reading many different accounts' records.

How to test for it#

  • Use at least two accounts per role (and two tenants if multi-tenant). Capture requests as user A and replay them with user B's session.
  • Change every identifier you can see: path segments, query parameters, JSON body fields, headers, and IDs embedded in encoded or signed-looking values.
  • Call admin and internal endpoints directly as a low-privilege user.
  • Try adding privileged fields (role, isAdmin, tenantId) to update requests.
  • Automate the matrix (role × endpoint × object owner) in integration tests so regressions are caught before release.

References#

Permalinkwww.breachvanelabs.com/library/broken-access-control/JSON
Found this in Breachvane? Your finding links here for background. Evidence, affected assets and retest status are in your Breachvane ↗ (opens in a new tab) workspace. Need help fixing it? Talk to us (opens in a new tab).