Blog / Authorization Testing

How to Test Tenant Isolation in Multi-Tenant SaaS APIs

In a multi-tenant SaaS product, one missing tenant filter can expose every customer’s data to every other customer. This playbook shows where tenant isolation breaks in APIs and how to test each of those places.

· 6 min read

Tenant isolation is the guarantee that one customer organization can never read, modify, or infer another organization’s data. In APIs, isolation is usually enforced by application code—a tenant filter on every query—so it fails the way application code fails: one forgotten condition in one handler. A cross-tenant leak is a form of IDOR/BOLA, but its impact is larger because it crosses a contractual boundary between customers.

This article was created with AI assistance. Test only environments you own or are authorized to test, with synthetic tenants and data.

Why tenant isolation fails in APIs

  • Filters live in handlers. When every query must remember AND tenant_id = ?, the one that forgets becomes a leak.
  • Tenant comes from the client. APIs that read the tenant from a header, subdomain, or body field—rather than from the verified token—let an attacker simply ask for another tenant.
  • Secondary paths skip the main code. Exports, reports, search indexes, caches, and background jobs often query data through different code paths.
  • Shared resources. Object storage buckets, cache keys, and queues shared across tenants leak when keys are predictable.
  • Admin and support tools. Internal impersonation or support endpoints exposed on the public API.

Isolation models and what to test in each

ModelHow data is separatedMain risk to test
Shared tablestenant_id column on every rowMissing filters in any query
Schema per tenantSeparate database schemasWrong schema selected from client input; shared lookup tables
Database per tenantSeparate databasesConnection routing from untrusted input; shared caches and storage
Row-level securityDatabase policies enforce filtersPolicies bypassed by privileged connections or background jobs

Stronger storage isolation reduces risk but does not remove it. Every model still depends on the API choosing the tenant from a trustworthy source.

Test setup

  1. Create two tenants, Tenant A and Tenant B, with identical role structures.
  2. In each tenant, create a user for every role: owner, admin, member, viewer, and any API-key or service identity.
  3. Populate both tenants with the same kinds of objects, and record every ID.
  4. Put a recognizable marker in Tenant B’s data (for example, a project named TENANT-B-CANARY) so leaks are easy to spot in responses, exports, and search results.

Where to test

  • Direct object access: every operation that takes an ID.
  • Collections: list, search, filter, and pagination endpoints.
  • Aggregates: dashboards, counts, analytics, and usage endpoints.
  • Exports: CSV, PDF, and report generation, including asynchronous jobs that email a link.
  • Files: upload, download, and pre-signed URL generation.
  • Invitations and membership: inviting users, accepting invites, and changing roles.
  • Integrations: webhooks, API keys, OAuth apps, and connected accounts.
  • Tenant selection: headers such as X-Tenant-ID, subdomains, and path prefixes like /orgs/{orgId}/.

Tenant isolation test playbook

1. Cross-tenant object access

Signed in as a Tenant A user, call every operation with Tenant B object IDs: read, update, delete, and any action endpoints. Expect 403 or 404.

GET /api/v1/projects/prj_B_2001 HTTP/1.1
Authorization: Bearer <tenant-a-member>

Expected: 404 Not Found
Leak:     200 OK  {"id":"prj_B_2001","name":"TENANT-B-CANARY",...}

2. Tenant selector tampering

Keep Tenant A’s token but change every tenant hint: X-Tenant-ID, /orgs/{orgId}/, ?organizationId=, and body fields such as tenantId. The server should ignore or reject anything that disagrees with the token.

3. Collection and search leakage

Search for Tenant B’s canary value from Tenant A. Remove or alter filter parameters, request very large pages, and walk pagination to the end. Full-text search indexes are a frequent source of leaks because they are populated outside the main data-access layer.

4. Cross-tenant references on write

Create or update a Tenant A object that references a Tenant B object: assign a Tenant B user to a Tenant A task, attach a Tenant B file, or move an object into a Tenant B project. A successful reference can leak data back to Tenant A or corrupt Tenant B.

5. Membership and invitation abuse

Try to invite yourself into Tenant B, accept another tenant’s invitation token, or change your own role or organization through a profile update (a mass assignment pattern).

6. Exports, files, and asynchronous jobs

Generate exports with Tenant B identifiers in the filter. Request download URLs for Tenant B files. Check whether pre-signed URLs or export links are guessable or reusable across tenants.

7. Function-level access across tenants

Confirm that a Tenant A admin is an admin only in Tenant A. Call admin operations with Tenant B identifiers using the Tenant A admin token. This combines BFLA and BOLA, and it is often missed because admin endpoints are tested only within one tenant.

8. Caches and rate limits

Request the same resource path from both tenants in quick succession and confirm responses are not served from a shared cache. Check that rate limits and quotas are tracked per tenant, so one tenant cannot exhaust another’s capacity.

Reading the results

  • Clear leak: Tenant B’s canary or identifiers appear in a response to Tenant A.
  • Existence oracle: different responses for “exists in another tenant” and “does not exist” (for example, 403 vs 404) let attackers enumerate other tenants’ objects.
  • Silent write: the request appears to fail, but reading the object as its Tenant B owner shows it changed. Always verify writes from the victim’s side.
  • Aggregate drift: counts or totals in Tenant A change when Tenant B data changes.

Making tenant isolation testing continuous

Isolation regressions are introduced one endpoint at a time, so a one-off test decays quickly. The durable approach is to encode tenants and roles once and test every operation on every release.

DastPilot does this from your OpenAPI spec or Postman collection: each role is linked to an authentication profile, credentials, and ownership identifiers, and the scanner replays operations with foreign identifiers to detect cross-user and cross-tenant access. Findings are compared across runs, so a reintroduced leak is flagged as reopened rather than buried in a new report. Trigger scans from your pipeline using the approach in API security testing in CI/CD.

Preventing cross-tenant leaks

  • Derive the tenant from the verified token or session, never from client input.
  • Enforce tenant scoping in one place: a repository layer, ORM scope, or database row-level security.
  • Make the unscoped query path difficult to use, so that bypassing tenant scoping is explicit and reviewable.
  • Include the tenant in every cache key, storage path, search index document, and queue message.
  • Return 404 for objects outside the caller’s tenant.
  • Log and alert on cross-tenant access attempts.

Frequently asked questions

What is tenant isolation in SaaS?

Tenant isolation is the guarantee that each customer organization in a shared SaaS application can access only its own data and resources. In APIs it is usually enforced by scoping every query and action to the tenant of the authenticated user.

How do you test for cross-tenant data leaks?

Create two tenants with identical roles and recognizable data, then call every API operation as a user in one tenant with identifiers and tenant hints from the other. Check responses, exports, search results, and the victim tenant’s data for any cross-tenant access or change.

Is a cross-tenant leak the same as BOLA?

It is a specific, high-impact form of BOLA (Broken Object Level Authorization). The object belongs to a different customer organization rather than a different user in the same organization.

Does database-per-tenant prevent isolation bugs?

It reduces the blast radius of a missing query filter, but the API can still route requests to the wrong tenant if it trusts client input, and shared caches, storage, and search indexes can still leak data.

Test tenant isolation on every release

Model your tenants and roles once in DastPilot and catch cross-tenant access before customers do. 30-day free trial, no credit card required.

Start Free Trial