Multi-tenant isolation means that every customer (tenant) on a shared platform can see and change only its own data, even though the code, servers and often the database are shared. Done right, it is enforced at a layer the application cannot accidentally skip, such as the database, and it is proven by automated tests that try to cross tenant boundaries. Done wrong, a single missing filter can expose every store on the platform at once.
This guide explains the main isolation models, how database-level controls like PostgreSQL row-level security work, where the subtle leaks hide, and what to ask a SaaS commerce or website platform before you trust it with your customers' data.
Why isolation is the promise behind every SaaS platform
When you run your store or company site on a SaaS platform, you are sharing infrastructure with other businesses. That is what makes SaaS affordable and easy to operate. The trade-off is that the boundary between tenants becomes the most important security control in the whole system.
The OWASP Multi-Tenant Security Cheat Sheet puts the risk plainly: in a multi-tenant application, a single vulnerability can expose all tenants' data, and misconfigurations can leak data across tenant boundaries. It lists cross-tenant data leakage, tenant impersonation, insecure direct object references (IDOR) and tenant context injection among the key risks.
These are not exotic threats. In the OWASP Top 10:2025, Broken Access Control keeps its position at number one, and OWASP reports that 100% of the applications tested were found to have some form of it. One of its classic examples is exactly the multi-tenant failure mode: viewing or editing someone else's account simply by supplying its identifier.
The three isolation models
There is no single correct architecture. Platforms choose a model based on security requirements, compliance needs, cost and operational complexity. OWASP summarizes the options this way:
| Model | Where the boundary sits | Trade-offs |
|---|---|---|
| Separate databases | Database and credential boundary | Strong when credentials, network access, admin paths and backups are also separate; highest operational cost |
| Separate schemas | Namespace and database-role boundary | Needs disciplined grants, role separation and careful migrations |
| Shared tables with row-level policies | Policy and row boundary | Needs enforceable tenant ownership on every tenant table, constrained request roles and negative-path tests |
Many platforms use a hybrid, for example shared tables for most tenants and dedicated resources for tenants with stricter requirements. Microsoft's Azure Architecture Center guidance on multitenant data adds two practical considerations: shared data stores are susceptible to the noisy neighbor problem, where one busy tenant slows others, and the isolation model affects backup and restore, since restoring a single tenant from a shared database may require restoring to a separate resource and recovering that tenant's data selectively.
Enforce isolation where it cannot be bypassed
The most common way isolation fails is not a sophisticated attack. It is a query that forgets its WHERE tenant_id = … clause, a new report written in a hurry, or a background job that runs without tenant context. If isolation depends on every developer remembering a filter in every query, it will eventually fail.
That is why the strongest designs move the rule below the application code. In PostgreSQL, row-level security (RLS) lets the database itself decide which rows a query may see or change. The PostgreSQL documentation describes three behaviors that matter for tenant isolation:
- Default deny. Once row security is enabled on a table, all normal access must be allowed by a policy. If no policy exists, a default-deny policy applies and no rows are visible or can be modified.
- Reads and writes are both covered. A policy's USING clause filters the rows a command can read, update or delete, while WITH CHECK controls which rows can be inserted or updated. Without a separate WITH CHECK, the USING expression is applied to both, so a tenant cannot write a row that belongs to someone else.
- Some roles bypass it. Superusers and roles with the BYPASSRLS attribute always bypass row security, and table owners normally do too unless the table is set to FORCE ROW LEVEL SECURITY.
That last point is where many real-world implementations go wrong. RLS protects nothing if the application connects as a superuser or as the table owner. OWASP's guidance is to serve ordinary tenant requests through a least-privileged role that is neither a superuser nor a BYPASSRLS role, to reserve privileged connections for migrations and explicitly authorized administrative jobs, and to check the role that is actually deployed rather than the one declared in configuration.
Scope tenant context to the transaction
RLS policies typically compare each row's tenant column with a tenant identifier set for the current request. That identifier must come from a server-verified identity, never from a value the client can freely change. Client-supplied tenant IDs should be treated as selectors only, and the platform must verify that the signed-in user is actually allowed to act in the selected tenant.
Connection pooling creates a subtle trap. A setting applied with a plain SET persists for the database session, so when a pool hands the same connection to the next request, the previous tenant's context can be inherited. OWASP recommends setting tenant context with SET LOCAL (or the transaction-local form of set_config) inside each transaction, re-establishing it for every transaction, and failing closed with an error when it is missing rather than falling back to an unscoped query.
The leaks that live outside the database
Database policies are the floor, not the whole building. Data also moves through caches, queues, file storage and APIs, and each of these can leak across tenants on its own:
- Caches. Include the tenant identifier in every cache key whose value varies by tenant, give intentionally shared entries an explicit global namespace, and authorize the request before serving a cached value.
- Background jobs and queues. A shared queue is not an isolation boundary. Bind tenant context to each message from the authenticated producer, and re-establish and re-authorize it in the consumer.
- Files and media. Partition tenant-owned objects with tenant-aware keys, containers or storage policies, and keep shared assets in a clearly separate namespace.
- APIs and identifiers. Check tenant ownership on every lookup. Random or opaque IDs help against enumeration, but they are defense in depth, not a substitute for authorization.
- Resource usage. Tenant-aware rate limits and quotas stop one tenant's traffic spike from degrading service for everyone else.
Prove it on every change
A guarantee that is never tested is a hope. Isolation deserves its own automated tests, and they should run through the same database role, connection path and pooling mode the application uses in production. OWASP's recommendations for verifying tenant isolation include:
- Test the authorization matrix. For every protected table, prove that cross-tenant operations are denied and same-tenant operations succeed.
- Discover coverage from the schema. Compare the list of tenant-scoped tables against the tables that actually have row security enabled and a policy defined, and fail the build when a new table is unclassified or unprotected. A hand-maintained list drifts: the same omission that leaves RLS off a new table can leave it out of the test.
- Assert the request role. Fail a configuration check if the deployed request role is a superuser or can bypass row security.
- Exercise connection reuse. Run requests for two tenants over reused connections and prove the second cannot observe the first one's context.
Running these checks as a required step before code is merged turns isolation from an assumption into something the team verifies continuously.
What to ask your platform provider
You rarely get to read a SaaS vendor's source code, but you can ask focused questions:
- Which isolation model do you use, and is tenant separation enforced at the database level or only in application code?
- Which database role serves ordinary requests, and can it bypass tenant policies?
- How are caches, background jobs and file storage separated by tenant?
- Are cross-tenant access tests automated, and do they block a release when they fail?
- How are secrets stored and rotated, and is two-step verification available for staff sign-ins?
- Can a single tenant's data be restored to an earlier point in time without affecting others?
How CIQRA approaches it
CIQRA keeps each site's and store's data isolated from every other at database level. Secrets live in Azure Key Vault and are rotated regularly. Team sign-in supports two-step verification, roles and permissions, and a log of who changed what. Data is hosted in Microsoft Azure data centers in Germany, and site backups come with restore preview and point-in-time database recovery.
FAQ
What is multi-tenant isolation?
It is the set of controls that keep each customer's data and actions separate on a platform where many customers share the same application and infrastructure.
Is row-level security enough on its own?
No. It is a strong database-level control, but caches, queues, file storage, APIs and privileged roles all need their own tenant boundaries, and the whole setup needs automated tests.
Is a separate database per tenant always safer?
It gives a clearer boundary, but only when credentials, network access, admin paths and backups are also separated, and it costs more to run. A well-built shared model with enforced policies and thorough testing can also be robust.
Sources
- OWASP Cheat Sheet Series, Multi-Tenant Application Security Cheat Sheet (2026)
- OWASP, Top 10:2025 A01 Broken Access Control (2025)
- PostgreSQL Global Development Group, PostgreSQL 18 Documentation: Row Security Policies (2025)
- Microsoft, Azure Architecture Center: Architectural approaches for storage and data in multitenant solutions (2026)