Broken object level authorization (BOLA)

Backend security Junior Python 3.13+ PostgreSQL 17+ Reviewed By Best Latest Apps

Spot the bug

Which line lets any signed-in user read any invoice?

  1. from dataclasses import dataclass
  2. ​
  3. ​
  4. @dataclass
  5. class Invoice:
  6. id: int
  7. owner_id: int
  8. total: int
  9. ​
  10. ​
  11. DB = {1001: Invoice(1001, 7, 500), 1002: Invoice(1002, 8, 900)}
  12. ​
  13. ​
  14. def get_invoice(user_id: int, invoice_id: int) -> Invoice | None:
  15. inv = DB.get(invoice_id)
  16. return inv
Hint 1 of 1

Where is user_id used?

Choose an answer, then open Show answer below to check it.

Show answer
  • Answer: line 16 return inv

Line 16 returns the invoice without comparing inv.owner_id to user_id. Return it only when they match.

30-second answer

BOLA happens when an endpoint takes an object id from the request and returns or changes that object without checking the caller may access it: change /invoices/1001 to /invoices/1002 and you see someone else's invoice. It tops the OWASP API Security Top 10 (2023). Fix it by checking ownership or tenancy on every object access, ideally inside the data-access layer (WHERE id = $1 AND tenant_id = $2), so no handler can forget.

Practise saying it · 30 s. No recording, just a timer

What is BOLA?

Say your answer out loud, then check which of these points you covered:

  • Id swap example
  • OWASP API #1
  • Scope every query
  • Cross-user tests
  • UUIDs aren't auth

Deep dive

Random UUIDs make guessing harder but are not authorization.

Write tests that call each endpoint as a second user and expect 404 or 403.

Return 404 rather than 403 when you don't want to reveal the object exists.

Batch and GraphQL endpoints need the same per-object check.

Common mistakes

  • Checking the user is logged in and nothing more.
  • Relying on unguessable ids.
  • Checking ownership in the UI only.

Senior take & follow-ups

Define it with the id-swap example, then the fix: scoped queries and cross-user tests.

Follow-up questions

Think of your answer first, then open the hint.

  1. Where should the check live?

    Show hint

    In the query or repository, scoped by owner or tenant.

  2. 403 or 404?

    Show hint

    404 hides existence; 403 is clearer for shared resources. Be consistent.

Suggest a fix if something here is wrong or out of date.