BOLA Vulnerabilities in Multi-Tenant SaaS: Real Examples and How to Test For Them
BOLA Vulnerabilities in Multi-Tenant SaaS: Real Examples and How to Test For Them
In modern cloud computing, multi-tenant software-as-a-service (SaaS) architectures dominate. Multiple companies and thousands of users share identical compute pipelines, application servers, and database clusters—relying on software-enforced logical boundaries to keep sensitive customer data segregated.
When those logical boundaries fail, the consequences are disastrous.
At the very center of multi-tenant vulnerabilities sits BOLA (Broken Object Level Authorization), formerly categorized as Insecure Direct Object References (IDOR). BOLA has maintained the undisputed #1 spot on the OWASP API Security Top 10 for good reason: it is trivial to exploit, catastrophic in impact, and completely invisible to standard automated vulnerability scanners.
In this guide, we examine what causes BOLA in multi-tenant systems, break down three illustrative real-world attack scenarios, explain why automated scanners fail to detect it, and walk through the manual testing methodologies security practitioners use to uncover authorization flaws before attackers do.
1. What is BOLA in a Multi-Tenant Context?
At its core, Broken Object Level Authorization (BOLA) occurs when an API endpoint accepts an object identifier from a client request and accesses the corresponding resource in the database without validating whether the authenticated user or tenant has permission to access that specific object.
The Anatomy of a BOLA Flaw
In a typical SaaS application, authorization logic must operate on two distinct levels:
- Authentication & Function-Level Authorization: "Is this request coming from a logged-in user who has permission to use the endpoint?" (Yes, they are an authenticated member).
- Object-Level Authorization: "Does this specific user or organization actually own the record with ID
rec_9921?" (If this step is missing or improperly verified, BOLA exists).
Client Request:
GET /api/v1/workspaces/ws_tenant_A/invoices/inv_987452
Authorization: Bearer <Tenant_A_User_JWT>
Server Code (Vulnerable):
invoice = db.query("SELECT * FROM invoices WHERE id = ?", params[:invoice_id])
return json(invoice) # CRITICAL: Fails to check WHERE org_id = current_user.org_id!
The "UUID Myth"
Many development teams believe replacing sequential integer IDs (/invoices/1024) with random UUIDv4 strings (/invoices/9f8c12a4-...) eliminates BOLA.
This is a dangerous misconception. While UUIDs prevent sequential enumeration (guessing ID 1025 after 1024), they do not enforce authorization. If an attacker discovers a competitor's UUID through shared links, API responses, client-side metadata, public webhooks, or error logs, the vulnerable endpoint will gladly hand over the data. Security through obscurity is not access control.
2. Three Illustrative Real-World Scenarios (Non-Client Architecture Examples)
To understand how BOLA manifests in production, let us analyze three architectural scenarios commonly uncovered during manual API penetration tests.
(Note: The following examples are generalized architectural models for educational and defensive purposes and do not represent any single organization's infrastructure).
Scenario 1: Tenant Data Bleed via API Parameter Tampering
- The Architecture: A B2B billing and invoicing SaaS platform.
- The Normal Flow: A user from Tenant A logs in, clicks their billing history, and their browser issues an authenticated request:
GET /api/v2/organizations/org_101/billing/statements/stmt_55410
Authorization: Bearer <Tenant_A_Token>
- The Exploit: The tester modifies the request in an intercepting proxy, keeping Tenant A's authorization token intact while substituting the statement ID with that of Tenant B (
stmt_55411):
GET /api/v2/organizations/org_101/billing/statements/stmt_55411
Authorization: Bearer <Tenant_A_Token>
- The Root Cause: The backend router validated that the requester had access to
org_101. However, the underlying SQL query for statements only matched on the primary key:
-- Insecure Query:
SELECT * FROM statements WHERE statement_id = 'stmt_55411';
-- Secure Query:
SELECT * FROM statements
WHERE statement_id = 'stmt_55411' AND organization_id = 'org_101';
Because the statement ID alone was used to query the record, Tenant A was able to extract the complete bank account numbers, customer names, and transaction totals of Tenant B.
Scenario 2: Asynchronous Background Export Job Hijacking
- The Architecture: An enterprise project management platform that generates heavy analytical reports via background task queues (e.g., Celery, Sidekiq, or AWS SQS).
- The Normal Flow: A project lead triggers an export job:
POST /api/v1/reports/export
{
"project_id": "proj_internal_sales",
"format": "csv"
}
- The Exploit: The API endpoint enqueues a background message containing the JSON body. A worker daemon pulls the job from Redis and generates a CSV report, uploading it to an Amazon S3 bucket with a signed URL emailed to the requesting user.
- The Root Cause: The API gateway checked that the user had an active session, but delegated the task processing entirely to the worker. The background worker ran with privileged administrative system credentials and never validated whether the user had read access to the specified
project_id. By simply providing another tenant's project ID, the attacker received a full dump of proprietary project data delivered straight to their inbox.
Scenario 3: Nested Resource Hierarchy Authorization Bypass
- The Architecture: A collaborative document management system with nested REST resources.
- The Normal Flow: Endpoints follow a hierarchical path:
GET /api/v1/workspaces/{workspace_id}/folders/{folder_id}/documents/{document_id}
- The Exploit: The tester swaps the
document_idwith a document belonging to a restricted folder or a separate tenant's workspace:
GET /api/v1/workspaces/my_workspace/folders/my_folder/documents/victim_confidential_doc
- The Root Cause: The engineering team implemented middleware that verified the user had access to
my_workspaceandmy_folder. However, the final document controller executed:
# Ruby on Rails vulnerable pattern:
@document = Document.find(params[:document_id])
Instead of scoping the search to the verified folder:
# Secure pattern:
@document = @folder.documents.find(params[:document_id])
The document was returned, bypassing the entire folder-level and workspace-level permission hierarchy.
3. Why Automated Vulnerability Scanners Universally Miss BOLA
Many engineering teams wonder why their commercial DAST and SAST tools never catch BOLA vulnerabilities. The reason is rooted in how scanners operate:
- Scanners Test Syntax, Not Business Semantics: Scanners excel at identifying technical syntax anomalies—injecting SQL syntax characters to trigger errors, or injecting cross-site scripting payloads to check for reflection. BOLA requests contain perfectly valid JSON, syntactically correct IDs, and valid authentication tokens.
- Scanners Lack Multi-User Context: Detecting BOLA requires establishing two or more distinct user sessions across different tenant boundaries (Tenant A Admin vs. Tenant B Member) and comparing the differential state of their responses. Automated tools typically crawl an application using a single authenticated session.
- Scanners Cannot Infer Data Ownership: When a scanner sends a request and receives an
HTTP 200 OKwith valid JSON data, it interprets the request as a success. A scanner has no semantic understanding that the returned customer record belongs to a completely different legal entity.
4. How Security Practitioners Test for BOLA (The Manual Playbook)
During professional penetration testing, offensive security practitioners utilize structured methodology to rigorously test for BOLA across all API surfaces:
Step 1: Establish the Multi-Tenant Matrix
Testers provision at least two completely isolated test organizations (Tenant A and Tenant B), each equipped with different role privileges:
- Tenant A (Administrator & Standard Member)
- Tenant B (Administrator & Standard Member)
Step 2: Traffic Mapping and Interception
Using tools like Burp Suite Pro or Caido, the tester maps every API route, parameter, and header utilized across both tenants.
Step 3: Differential Authorization Testing (Token Swapping)
Using tools like Burp's Match & Replace, Auto-Repeater, or custom scripts, the tester automatically replays every request initiated by Tenant A, but swaps the authentication header with the Bearer token of Tenant B:
- If the server returns
HTTP 403 ForbiddenorHTTP 404 Not Found, authorization controls are functioning correctly. - If the server returns
HTTP 200 OKwith the requested resource, a BOLA vulnerability is confirmed.
Step 4: Testing Write and Delete Methods
Testers test beyond read-only GET requests. They probe PUT, PATCH, and DELETE methods to determine if cross-tenant modification or deletion is possible (e.g., deleting another company's team member or modifying webhook target URLs).
5. Architectural Defense: How to Permanently Prevent BOLA
Fixing BOLA requires architectural discipline rather than ad-hoc code patches:
- Enforce Tenant-Scoped Data Access at the ORM Layer:
Never query an entity by primary key alone. Always scope queries to the authenticated tenant:# Vulnerable: order = Order.objects.get(id=order_id) # Secure: order = Order.objects.get(id=order_id, organization=request.user.organization) - Leverage Database Row-Level Security (RLS):
Databases like PostgreSQL support native Row-Level Security (RLS). By setting a tenant context variable on every database connection, the database engine automatically filters out records belonging to other tenants, even if backend application code contains a bug. - Centralize Policy Enforcement:
Decouple authorization checks from endpoint controllers using policy engines like Open Policy Agent (OPA), AWS Cedar, or framework-level policy classes (e.g., Pundit or CASL).
Ensure Your Multi-Tenant SaaS is Defended Against BOLA
Automated scanners will not protect your platform from complex authorization and business logic flaws. Expert-led manual penetration testing is the only reliable way to validate tenant isolation boundaries before shipping to production.
Schedule a confidential 20-minute scoping review with TrustLayerLabs to evaluate your API architecture and eliminate authorization blind spots.
Ready to Identify & Fix Vulnerabilities in Your Platform?
Schedule a confidential 20-minute scoping review with our lead security architects under mutual NDA. We evaluate your APIs, business logic, and enterprise readiness.