top of page

BOLA Vulnerabilities Explained in API Security

Writer: Yashika Wadhwani
Yashika Wadhwani
12 minutes ago
8 min read

A single predictable ID can expose someone else’s data if the API trusts the user too much. That is the core risk behind Broken Object Level Authorisation, usually shortened to BOLA.


BOLA is one of the most common and damaging API security flaws because it hides in normal API behaviour. The request looks valid. The endpoint exists. The user is authenticated. The problem is that the API fails to check whether that user is allowed to access the specific object being requested.


For example, a mobile app may call: GET /api/v1/orders/45821


If changing `45821` to `45822` returns another customer’s order, the API has a BOLA issue.


This post explains how BOLA works, why it is so common, how to test for it safely, and how engineering teams can prevent it in production APIs.


Wide-angle view of a locked server rack with numbered request tags hanging from the cables.
BOLA often starts with a simple object identifier that the API trusts too much.

What BOLA means in API security


BOLA happens when an API exposes an object through an identifier and does not properly verify ownership or permission for that object.


An object can be almost anything the application manages:


  • A user profile

  • An invoice

  • A bank account

  • A medical record

  • A support ticket

  • A delivery address

  • A file attachment

  • A chat message

  • A project, team, or workspace


The “object level” part matters. Authentication only proves who the user is. Authorisation decides what that user can access.


A user being logged in is not enough. The API must ask a second question on every sensitive object request:


Is this authenticated user allowed to perform this action on this specific object?


If the answer is not checked clearly and consistently, the API may leak or modify data belonging to other users.


BOLA Vulnerabilities are also known as IDOR, which stands for Insecure Direct Object Reference. IDOR is the older and more general term. BOLA is used widely in API security because modern APIs often expose object IDs through REST paths, query strings, GraphQL arguments, mobile traffic, and JSON request bodies.


Why BOLA is so common


BOLA is common because APIs are designed to expose structured data. IDs are part of normal API design.


A frontend often needs to request a specific resource:


GET /api/v1/users/7312
GET /api/v1/accounts/acc_9041/statements
GET /api/v1/projects/120/files

That is not unsafe by itself. The risk appears when the backend treats the supplied ID as proof of permission.


A common weak pattern looks like this:

user = authenticate(token)
order = find_order(request.path.order_id)
return order

The safer pattern adds an authorisation decision:

user = authenticate(token)

order = find_order(request.path.order_id)

if not can_access(user, order):

    return 403

return order

The missing check may seem small, but the impact can be severe.


BOLA also appears because teams split responsibilities. Frontend developers may hide buttons for unauthorised actions. Backend developers may assume the frontend will not send forbidden IDs. Attackers do not use the frontend as intended. They send direct API requests.


Other common causes include:


  • Sequential numeric IDs such as `1001`, `1002`, `1003`

  • Shared endpoints used by many user roles

  • Reused controller logic without permission checks

  • Complex tenant, organisation, or workspace models

  • Over-trust in JWT claims

  • Missing tests for negative access cases

  • GraphQL resolvers that fetch objects directly

  • Admin and customer APIs sharing service methods


The issue is not the use of IDs. The issue is using IDs without a reliable permission check.


How a BOLA flaw usually looks


A BOLA test often starts with two accounts. Account A owns one object. Account B owns another. The tester captures a request from Account A and replaces the object ID with the ID from Account B.


Here is a simple example.


Account A requests its own profile document:

GET /api/v1/documents/doc_81K2
Authorization: Bearer token_for_account_a

The API returns:

{
  "id": "doc_81K2",
  "ownerId": "user_a",
  "fileName": "aadhaar-upload.pdf"
}

Now the tester changes the ID:

GET /api/v1/documents/doc_92P7
Authorization: Bearer token_for_account_a

If the API returns Account B’s document, it is vulnerable.


The same issue can affect write operations:

PATCH /api/v1/addresses/address_5009

Authorization: Bearer token_for_account_a

Content-Type: application/json

	{

  		"city": "Pune"

	}

If Account A can update Account B’s address by changing the ID, the bug is more than a data leak. It becomes unauthorised modification.


BOLA can also show up in less obvious places.


Query parameters


GET /api/v1/reports?userId=7312

If a normal user can change `userId`, the API must verify that the requested user is within their permission scope.


Request bodies

{
  "accountId": "acc_2044",
  "amount": 5000
}

The backend should not blindly trust `accountId` from the client.


Nested resources

GET /api/v1/companies/88/users/991

The API must check access to the company and the user, not just whether both exist.


GraphQL arguments

query {
  invoice(id: "inv_7721") {
    id
    total
    customerEmail
  }

}

Every resolver that returns object data needs an authorisation check.


BOLA impact depends on the object


Not every BOLA issue has the same severity. The risk depends on the object type, data sensitivity, and allowed action.


A read-only issue may expose names, phone numbers, order history, tax details, invoices, or documents. In India, this could involve personal data such as addresses, identity documents, payment references, or employment records. That can become a privacy, compliance, and trust problem.


A write-level issue can be worse. Attackers may change delivery addresses, cancel bookings, alter profile details, add users to teams, or connect external accounts.


A delete-level issue can disrupt business operations: DELETE /api/v1/projects/120


If the API only checks whether the user is logged in, a low-privilege user could delete objects outside their scope.


The most serious cases involve money movement, account takeover paths, medical data, legal records, or internal admin objects.


A helpful way to assess impact is to ask:


  • What data does the object contain?

  • Can the object be changed or deleted?

  • Can access to this object lead to another sensitive action?

  • Does the object belong to another user, organisation, tenant, or role?

  • Would the affected user expect this data to remain private?


A practical prompt for reviewing an API for BOLA


Use the following technical prompt when reviewing API endpoints, writing test cases, or guiding an internal security assessment.


Review the following API endpoint for Broken Object Level Authorisation.


Endpoint:

[HTTP method and path]


Request example:

[Headers, path parameters, query parameters, and JSON body]


Authenticated user context:

[User ID, role, tenant, organisation, subscription, or ownership details]


Object being accessed:

[Object type, object ID, owner ID, tenant ID, sensitivity level]


Expected permission rule:

[Who should be able to read, create, update, delete, or list this object]


Analyse the endpoint for BOLA risk.


Check whether:

  1. The endpoint accepts object identifiers from the client.

  2. The backend verifies that the authenticated user can access the exact object.

  3. The check covers tenant, organisation, role, ownership, and relationship rules.

  4. The check happens before returning, changing, or deleting data.

  5. The same rule is enforced for REST, GraphQL, mobile, and internal API paths.

  6. The endpoint prevents horizontal access between users at the same role.

  7. The endpoint prevents vertical access from low-privilege users to admin objects.

  8. The endpoint handles nested resources securely.

  9. Error responses do not leak whether forbidden objects exist.

10. Automated tests cover allowed and denied access cases.


Return:

  • A clear risk rating

  • The likely vulnerable parameters

  • The required authorisation checks

  • Safe test cases using two controlled test accounts

  • Recommended code-level fixes


This prompt is useful because it forces the review to look beyond authentication. It focuses on the exact object, the exact user, and the exact permission rule.


How to test for BOLA safely


BOLA testing should use accounts and objects that you own or have explicit permission to use. This matters in internal security reviews, staging environments, and any Bug Bounty work.


A safe test setup usually includes:


  1. Create two test users

    Use Account A and Account B under controlled conditions.


  2. Create separate objects

    For example, each account creates its own order, file, ticket, or profile record.


  3. Capture valid requests

    Use browser developer tools, a proxy, API logs, or approved testing tools.


  4. Swap object identifiers

    Replace Account A’s object ID with Account B’s object ID.


  5. Observe the response

    A secure API should return `403 Forbidden`, `404 Not Found`, or another controlled denial response.


  6. Repeat for actions

    Test read, update, delete, list, export, and share operations if they are in scope.


The expected result is simple: Account A should not be able to access Account B’s object unless the business rule clearly allows it.


Be careful with production systems. Do not access real user data. Do not change or delete records. Use test accounts, low-risk objects, and a clearly approved scope.


Close-up view of two labelled access cards beside a hardware security key on a metal surface.
Safe BOLA testing uses controlled accounts, not real user data.

How to prevent BOLA in API design


The best BOLA defence is strict server-side authorisation. Frontend checks help user experience, but they do not protect the API.


Check permissions on every object request


Every endpoint that reads, updates, deletes, exports, shares, or lists objects should enforce object-level access.


A good rule looks like this:

function getInvoice(user, invoiceId):
    invoice = invoiceRepository.findById(invoiceId)
if invoice is null:
        return 404
    if not policy.canReadInvoice(user, invoice):
        return 403
return invoice

The policy should live in a consistent layer, not scattered across controllers.


Avoid trusting client-supplied ownership fields


Do not let the client decide ownership:

{
  "ownerId": "user_123"
}

Use the authenticated user context from the server instead:

ownerId = authenticatedUser.id

For organisation or tenant systems, resolve membership on the server. Do not accept `tenantId` as proof that the user belongs to that tenant.


Use policy-based access control


Centralised policies reduce mistakes. For example:

canUpdateProject(user, project):
    return user.tenantId == project.tenantId
       and user.role in ["admin", "manager"]

This is easier to test than custom logic inside every controller.


Test denied access cases


Many teams only test the happy path. BOLA prevention needs negative tests.


For each sensitive endpoint, add tests such as:


  • User can access their own object

  • User cannot access another user’s object

  • User cannot access another tenant’s object

  • Low-privilege user cannot access admin object

  • Removed team member cannot access old team resources

  • Disabled user cannot continue using cached tokens


These tests should run in CI along with normal API tests.


Common mistakes that leave APIs exposed


Teams often add partial fixes that look safe but still leave gaps.


Mistake

Why it fails

Better approach

Hiding IDs in the frontend

Attackers can inspect traffic

Enforce backend permission checks

Using UUIDs only

Harder to guess does not mean authorised

Check access for every object

Checking role but not ownership

Same-role users may access each other’s data

Check role and object relationship

Checking parent but not child object

Nested resources may cross boundaries

Validate the full resource chain

Returning full objects before filtering

Sensitive data may already be exposed

Filter in the query or service layer

Relying on API gateway authentication

Gateways rarely know object ownership

Add object checks in the application


UUIDs, gateways, and frontend controls can help, but they are not substitutes for authorisation logic.


What secure behaviour looks like


A secure API treats every object ID as untrusted input. It does not matter whether the ID came from a URL, query string, body, cookie, mobile app, or GraphQL request.


Secure behaviour includes:


  • The API authenticates the user.

  • The API loads the requested object carefully.

  • The API checks the user’s permission for that exact object.

  • The API denies access before returning sensitive data.

  • The API logs suspicious denied access patterns.

  • The API uses consistent error handling.

  • The API tests both allowed and denied paths.


For sensitive applications, logs should record denied object access attempts without storing unnecessary personal data. Patterns such as repeated access to sequential IDs may signal probing.


The key takeaway


BOLA is dangerous because it looks like ordinary API traffic. The request is valid, the token is valid, and the endpoint works. The failure sits in the missing question: should this user access this object?


The most reliable fix is not hiding IDs or using longer random strings. Build object-level authorisation into the backend, apply it consistently, and test it with controlled accounts.


If an API accepts an object ID, assume it needs an authorisation check. That single habit prevents a large class of serious API security failures.


 
 
 

Comments


Get Started with Listing of your Bug Bounty Program

  • Black LinkedIn Icon
  • Black Twitter Icon
bottom of page