BIPI
BIPI

Pentesting SaaS Tenant Isolation: A Two-Tenant Methodology

Cybersecurity

Multi-tenant SaaS is one IDOR away from a data breach headline. The reliable way to find isolation failures is to set up two real tenants and look for cross-tenant bleed.

By Arjun Raghavan, Security & Systems Lead, BIPI · March 15, 2025 · 8 min read

#multi-tenant#saas-security#pentest

Multi-tenant SaaS pentests are different from single-app pentests. The vulnerability class is cross-tenant data leakage, and the only reliable way to find it is to provision two tenants and look for any way that tenant A can read or write tenant B. The setup matters more than the tooling.

Methodology: the two-tenant setup

Sign up for two paid tenants under different organizations. Give each tenant identifiable test data: tenant-A-secret-data, tenant-B-secret-data. Use Burp with two simultaneous browser profiles, one logged in as each tenant. Match-and-replace requests between tenants to test isolation at every endpoint.

  • Identify the tenant identifier in requests: subdomain (acme.app.com), path segment (/t/acme/), header (X-Tenant-Id), or JWT claim.
  • Swap tenant A's tenant identifier into a tenant B request and observe whether the response changes.
  • Test object IDs across tenants: if tenant A creates user 1234, can tenant B GET /users/1234 and read it?
  • Test JWT claim manipulation: re-sign the JWT with the other tenant's ID if the signature is weak or none.
  • Test cache keys: requests that omit tenant context may hit a shared cache namespace.

IDOR across tenants is the most common finding

The pattern: an endpoint like GET /api/documents/:id queries the database by document ID alone. The application enforces 'is this user logged in' but not 'does this document belong to the user's tenant'. Sequential or guessable IDs make exploitation trivial; UUIDs slow but do not stop a determined attacker who can leak IDs through other channels (search APIs, email links, public sharing features).

JWT and shared cache risks

JWTs that include tenant_id in the payload need that claim verified server-side on every request. We have seen apps where the JWT is decoded for the user_id but the tenant_id is read from a URL parameter, allowing trivial cross-tenant access. Shared caches (Redis, Memcached, CDN) keyed without tenant context are a quieter version of the same issue: tenant A's response gets cached and returned to tenant B.

Detection

Application logs should record tenant_id, user_id, and resource_id on every authenticated request. Alert on access patterns where user_id from tenant A queries a resource_id whose owner_tenant_id is not A. WAFs cannot detect this; it requires application-aware logging in the data layer.

Remediation

  1. Embed tenant_id in the authentication token (JWT, session). Verify it on every request server-side. Never read tenant context from a URL parameter or header alone.
  2. Enforce tenant scoping at the database layer with row-level security (Postgres RLS) or query-time tenant filters in the ORM. Defense in depth beyond the application code.
  3. Cache namespaces must include tenant_id. Redis keys: tenant:{id}:resource:{id}. CDN cache keys must vary on tenant context.
  4. Sign embedding URLs with HMAC that includes tenant_id, user_id, resource_id, and an expiration. Reject expired or tampered signatures.
  5. Use UUIDs for resource IDs to prevent enumeration. UUIDs alone are not authorization; combine with tenant scoping.
  6. Run a CI test that provisions two tenants and asserts cross-tenant access returns 403 on every endpoint.
  7. Log tenant context on every request and alert on cross-tenant access attempts in real time.

Read more field notes, explore our services, or get in touch at info@bipi.in. Privacy Policy · Terms.