In a multi-tenant SaaS product, one missing tenant check in one query exposes every customer at once, so tenant isolation is an authorisation problem first and an infrastructure problem second. Most cross-tenant leaks do not come from broken encryption or shared hardware; they come from code that trusts a tenant identifier from the request, a cache key without a tenant, a background job running with a privileged database role, or a support tool that can see everything. This guide shows how to carry verified tenant context from authentication to the database, which enforcement layers to combine, how to choose between silo, pool and bridge designs, and how to test and monitor isolation. It is for SaaS architects, application security engineers and engineering leaders. For the wider SaaS security programme see the Cybersecurity for SaaS Companies Guide, and for infrastructure-level separation see the Multi-Tenant SaaS Architecture Security Guide.
Key takeaways
- Tenant context must come from the server, not the client. A tenant ID in a header, path or body is a selector; authorisation must confirm the authenticated principal is a current member of that tenant.
- Defence in depth across layers. Combine an application check, a policy engine or middleware rule, and a data-layer control such as PostgreSQL row-level security or a partition-key IAM condition, so a single bug does not leak data.
- Privileged paths are where isolation fails. Superuser or BYPASSRLS database roles, cross-tenant admin consoles, background jobs and support impersonation need explicit authorisation and logging.
- Shared resources leak too. Caches, queues, object storage, search indexes, signed URLs and AI context stores all need tenant-aware keys and checks.
- Prove isolation with negative tests. A two-tenant authorisation matrix in CI, coverage checks for every tenant-scoped table and connection-reuse tests catch regressions before customers do.
Why tenant isolation is an authorisation problem
The OWASP Multi-Tenant Application Security Cheat Sheet lists the core risks: cross-tenant data leakage, tenant impersonation, broken isolation at database, cache, storage or compute layers, insecure direct object references, tenant context injection, shared resource poisoning, privilege escalation across tenants, and incomplete onboarding or offboarding. Almost all of them reduce to an authorisation decision that was skipped or made on untrusted input.
OWASP ASVS 5.0 makes this explicit. Requirement V8.4.1 asks that multi-tenant applications use cross-tenant controls so consumer operations never affect tenants they have no permissions for, V8.3.1 requires authorisation to be enforced at a trusted service layer rather than relying on controls a client could manipulate, and V8.2.2 requires data-specific access to be restricted to consumers with explicit permissions. The OWASP API Security Top 10 2023 entry API1 Broken Object Level Authorization adds that comparing the session user ID with an ID parameter is not, on its own, a sufficient fix: you must check the principal's rights to the specific object, which in SaaS includes the object's tenant. See the OWASP API Top 10 API1 Broken Object Level Authorization Guide.
Establishing trusted tenant context
- Derive, then verify: obtain the tenant from a verified token claim or session, then confirm current membership or service authorisation. OWASP's guidance is to treat client-supplied tenant identifiers as selectors only and bind tenant context to a server-verified identity.
- Use immutable identifiers: Microsoft's multitenant architecture guidance recommends immutable identifiers for both user and tenant (for example an Entra object ID rather than a UPN, which can change).
- Validate the issuer per tenant: for applications accepting tokens from many customer identity providers, validate issuer and tenant together. Microsoft states that multitenant applications using the
/commonor/organizationskey endpoints must check that the issuer in the metadata matches the tokenissclaim, in addition to the tenant ID check. Keep an allow list of onboarded tenant IDs and reject others. - Set context once, early: establish tenant context in middleware, store it request-locally, and stop downstream code from replacing it with unverified input.
- Handle users in many tenants: a consultant can be an administrator in one tenant and a viewer in another. Roles must be evaluated per tenant membership, never as a global role on the user, and the active tenant must be switched explicitly.
- Do not rely on opaque IDs: random identifiers reduce enumeration, but OWASP is clear they are not authorisation controls.
Isolation models: silo, pool and bridge
The AWS SaaS Lens defines three models. In a silo model tenants get dedicated resources, while still sharing identity, onboarding and operations; in a pool model tenants share resources; and a bridge model mixes the two per component, for example siloing a service because of its data's regulatory profile while pooling others.
| Data isolation option | Boundary | Strength | Cost and complexity | Typical fit |
|---|---|---|---|---|
| Separate database or account per tenant (silo) | Credentials, network and backups | Strongest, if credentials and admin paths are also separate | Highest: per-tenant migrations, monitoring and capacity | Regulated or very large tenants, data residency |
| Separate schema per tenant | Namespace and database roles | Good with disciplined grants and search_path handling | Medium: migrations multiply with tenants | Mid-market B2B with moderate tenant counts |
| Shared tables with tenant column and RLS (pool) | Row policy | Good if every tenant-owned table has policy and request roles cannot bypass it | Lowest running cost; needs strong testing | High tenant counts, self-service SaaS |
| Hybrid (bridge) | Varies by data class or tier | Matches risk to tier | Highest design complexity | Premium tier siloed, standard tier pooled |
Whatever the model, the authorisation layer is still needed: a siloed database does not help if the application connects to the wrong one because it trusted a request parameter.
Enforcement layers and how to combine them
| Layer | Mechanism | What it catches | Limits |
|---|---|---|---|
| Gateway or middleware | Resolve and verify tenant context; reject requests where token tenant and route tenant differ | Tenant context injection | Cannot see object ownership |
| Application or policy engine | RBAC per tenant membership, resource-based or relationship-based rules with tenant as a mandatory attribute | BOLA, function-level escalation inside a tenant | Every query path must call it |
| Data access layer | ORM default filters or tenant-aware sessions that inject the tenant predicate | Forgotten WHERE tenant_id | Raw SQL and new code paths can bypass |
| Database | PostgreSQL RLS with FORCE ROW LEVEL SECURITY; per-tenant schemas or databases | Application bugs that reach the database | Superuser and BYPASSRLS roles bypass; integrity checks are not filtered |
| Cloud data services | Short-lived credentials scoped to a tenant, for example DynamoDB dynamodb:LeadingKeys conditions on the partition key | Code that queries the wrong partition | AWS notes Scan returns all items regardless of leading keys, so it must be excluded |
PostgreSQL row-level security in practice
- Default deny: PostgreSQL documents that if RLS is enabled and no policy exists for the table, a default-deny policy applies and no rows are visible or modifiable.
- Owners and privileged roles: table owners bypass RLS unless
FORCE ROW LEVEL SECURITYis set, and superusers and roles withBYPASSRLSalways bypass it. Run the request path as a least-privileged role and checkrolsuperandrolbypassrlsinpg_rolesfor the deployed role, not just configuration files. - USING and WITH CHECK:
USINGfilters what can be read or targeted,WITH CHECKcontrols what can be written. Without an explicitWITH CHECKtheUSINGexpression is applied to new rows, which is usually what you want for tenant columns. - Restrictive policies: permissive policies combine with OR; add the tenant rule as a
RESTRICTIVEpolicy so later permissive policies (for sharing features) cannot widen access across tenants. - Transaction-scoped context: set the tenant setting per transaction, not per connection, so a pooled connection cannot carry one tenant's context into another's request.
- Covert channels: unique, primary key and foreign key checks bypass RLS, so a uniqueness error can reveal that a value exists in another tenant. Scope unique constraints to include
tenant_id.
For policy engine design, see the Authorisation Models Guide and the Policy-as-Code for Authorisation Guide.
Shared resources beyond the database
- Caches: classify each cached value as global, tenant-scoped or user-scoped, include the tenant ID in every tenant-scoped key, and authorise before returning a cached value, as OWASP recommends. User IDs are not guaranteed unique across tenants.
- Queues and background jobs: derive tenant context from the authenticated producer, bind it to the message with integrity protection or trusted broker routing, and re-establish and re-check it at the consumer, especially where execution is delayed and membership may have changed.
- Object storage and signed URLs: partition by tenant, authorise the exact object and operation before generating a signed URL, and keep URLs short-lived and method-specific.
- Search indexes, analytics and exports: these often copy data out of the protected database into stores with no tenant policy. Apply the same filters or separate indexes per tenant.
- AI features: retrieval indexes, embeddings and conversation memory are shared resources too; filter retrieval by tenant before the model sees content. See the Embedded AI in SaaS Guide.
- Rate limits and quotas: include tenant as a rate-limit dimension so one tenant cannot exhaust shared capacity for others.
Non-human identities, admin paths and support access
The highest-risk principals in a SaaS platform are not customer users but the platform's own identities that are allowed to cross tenants.
- Tenant API keys and integrations: bind each API credential to an explicit tenant, environment and scope. OWASP recommends generating tenant API credentials with at least 128 bits of entropy, returning them once and storing only a hash. See the SaaS API Token Security Guide.
- Stored customer tokens: if your product holds OAuth tokens for customers' other SaaS systems, that store is a cross-tenant blast radius. In the Salesloft Drift Breach 2025, integration tokens held in the vendor's cloud environment were stolen, and Google Threat Intelligence Group cited over 700 potentially affected organisations. Encrypt per tenant, restrict which workloads can decrypt, and monitor token use.
- Cross-tenant service identities: billing, reporting and migration jobs that legitimately touch many tenants should use separate identities, explicit tenant sets and their own audit trail, never the general request role.
- Support impersonation: Microsoft's guidance treats support impersonation as a privileged, auditable capability. Require a ticket reference, customer consent where contracts demand it, time limits and session recording, and show customers the access in their own audit log.
- Platform administrators: manage cross-tenant admin consoles as privileged access with phishing-resistant MFA and just-in-time elevation.
Choosing a design: decision guidance
| Question | If yes | If no |
|---|---|---|
| Do contracts or regulators demand physical or key-level separation? | Silo the affected data, or use per-tenant encryption keys | Pool with RLS and strong testing |
| Will you have thousands of small tenants? | Pool; silo per tenant becomes operationally impossible | Schema or database per tenant is manageable |
| Do users belong to several tenants? | Model membership explicitly with per-tenant roles and a tenant switcher | Still bind every session to one tenant |
| Do customers bring their own identity provider? | Per-tenant federation configuration, issuer allow list and domain verification | Central identity with tenant claim |
| Is relationship-based sharing across tenants a product feature? | Relationship-based model with tenant boundaries as explicit relations and restrictive default | Simple tenant-scoped RBAC |
Start-ups usually begin pooled; the key is to make the tenant predicate impossible to forget from day one, because retrofitting RLS into a mature schema is far harder than starting with it. Larger providers typically run a bridge model with a premium siloed tier.
Implementation and testing roadmap
- Classify data: mark every table, bucket, index, cache and queue as global, tenant-scoped or user-scoped.
- Centralise tenant context: one middleware that verifies token, issuer, tenant membership and sets request-local context.
- Add data-layer enforcement: enable and force RLS on tenant tables (or equivalent), switch request paths to a non-bypass role, include
tenant_idin unique constraints. - Build a two-tenant test suite: for every endpoint and tenant table, assert same-tenant success and cross-tenant denial; add coverage checks comparing the tenant-scoped inventory with
pg_class.relrowsecurityandrelforcerowsecurity, and a connection-reuse test, as OWASP recommends. See the Authorisation Testing Automation Guide. - Roll out in audit mode first: log what new policies would deny before enforcing, investigate every hit, then enforce per service; keep the previous policy version ready to restore if legitimate traffic is blocked.
- Harden privileged paths: separate roles for migrations and cross-tenant jobs, support impersonation workflow, admin console controls.
- Test offboarding and restore: on offboarding, block new operations and revoke sessions and API keys; test restoring one tenant from backup and confirm another tenant's records are absent.
- Pen test for tenant crossing: make cross-tenant access an explicit objective in every penetration test and bug bounty scope.
Detection and measurement
- Tenant mismatch events: log and alert whenever token tenant, route tenant and resource tenant disagree, even if the request was denied: repeated mismatches indicate probing.
- Privileged access: log every use of cross-tenant roles, BYPASSRLS connections and support impersonation with the tenant affected.
- Anomalous volume: watch for a single principal reading many tenants or unusual export volumes, mapped to MITRE ATT&CK T1213 Data from Information Repositories and T1530 Data from Cloud Storage; stolen integration credentials appear as T1078 Valid Accounts.
- Tenant-visible audit: provide each tenant its own audit log of access, including vendor support access.
| Metric | Target |
|---|---|
| Tenant-scoped tables with RLS enabled and forced | 100% |
| Endpoints covered by cross-tenant negative tests | 100% of tenant-scoped endpoints |
| Request-path database roles with superuser or BYPASSRLS | Zero |
| Support impersonation sessions with ticket and recording | 100% |
| Cross-tenant findings from testing and bug bounty, time to fix | Fixed within your critical SLA |
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| OWASP ASVS 5.0 | V8.4.1, V8.3.1, V8.2.1, V8.2.2, V8.2.3, V8.1.1 | Cross-tenant controls, trusted enforcement, function, data and field-level access, documented rules |
| OWASP API Security Top 10 2023 | API1 Broken Object Level Authorization | Object and tenant ownership checks |
| NIST SP 800-53 Rev. 5 | AC-3 Access Enforcement; AC-6 Least Privilege; AC-4(21) Physical or Logical Separation of Information Flows | Tenant-scoped enforcement and privileged roles |
| NIST SP 800-53 Rev. 5 | SC-4 Information in Shared System Resources; SC-7(21) Isolation of System Components; SC-39 Process Isolation | Caches, shared compute and silo components |
| NIST CSF 2.0 | PR.AA-05, PR.DS-01 | Least-privilege authorisation and data-at-rest protection |
| ISO/IEC 27001:2022 Annex A | 5.15 Access control; 8.3 Information access restriction | Access rules per tenant |
Common failure patterns
- Trusting
X-Tenant-ID: the tenant comes from a header or path without a membership check; parameterised queries prevent injection, not cross-tenant access. - Global roles: a user's admin role in one tenant is evaluated as admin everywhere.
- RLS enabled, but the app connects as owner or superuser: policies exist and are silently bypassed.
- Tenant-less cache keys: a profile or permission cached by user ID is served to a same-numbered user in another tenant.
- Background jobs with platform credentials: a job processes a message whose tenant field was never verified.
- Unscoped integration token stores: one compromise of the vendor environment exposes every customer's connected systems, as the Salesloft Drift case showed.
Practitioner checklist
- Derive tenant context from verified tokens and confirm current membership on every request.
- Validate issuer and tenant together for multi-tenant identity provider integrations, against an allow list.
- Evaluate roles per tenant membership, never globally.
- Enforce tenant isolation in at least two layers, including the data layer.
- Run request paths with roles that cannot bypass row-level security, and verify in production.
- Make every cache, queue, index and storage key tenant-aware.
- Bind API keys, service identities and stored customer tokens to explicit tenants and protect them as privileged credentials.
- Run two-tenant negative tests in CI and include tenant crossing in every pen test.
- Log and alert on tenant mismatches, cross-tenant roles and support impersonation.
Standards and references
- OWASP: Multi-Tenant Application Security Cheat Sheet
- OWASP: Application Security Verification Standard 5.0
- OWASP: API1:2023 Broken Object Level Authorization
- PostgreSQL: Row Security Policies
- AWS: SaaS Lens, Silo, Pool, and Bridge Models
- AWS: Using IAM policy conditions for fine-grained access control in DynamoDB
- Microsoft Learn: Architectural considerations for identity in a multitenant solution
- Microsoft Learn: Convert a single-tenant app to multitenant on Microsoft Entra ID
Related NHI Mgmt Group resources: Cybersecurity for SaaS Companies Guide · Multi-Tenant SaaS Architecture Security Guide · Authorisation Models Guide · Externalised Authorisation Buyer's Guide