Compare tenant-isolation boundaries by blast radius, authorization, restore, cost, noisy-neighbor risk, and migration complexity, then reproduce a missing-tenant-predicate data leak.
Tenant Isolation by Key, Collection/Table, Database, Cluster, or Account
The same authorization model now crosses tenant boundaries. AtlasMart compares shared and dedicated isolation while making a missing-tenant-route leak observable.
Compare tenant isolation at key, namespace/table, database, cluster, and account/project boundaries.
Evaluate blast radius, noisy-neighbor behavior, authorization, key isolation, backup/restore, migration, and cost together.
Reproduce a cross-tenant disclosure caused by a missing tenant predicate and repair it with scoped identity/routing.
Choose isolation boundaries from risk and operations evidence instead of assuming physical separation is always best.
1. Multi-tenancy is an authorization and routing problem before it is a schema pattern
AtlasMart serves many business customers. Two tenants can both
legitimately have an order named o-1. If the
application treats order_id alone as globally
unique, a lookup can cross tenant boundaries even when the
database itself is functioning perfectly. A
tenant boundary therefore needs to appear in
identity, authorization, key design, routing, observability,
backup/restore procedures, and incident response—not only in a
tenant_id column.
2. Isolation is a spectrum with different blast radii
A shared-key design co-locates tenants and relies heavily on correct predicates and authorization. Separate collections/tables or databases create stronger namespace boundaries but may still share process memory, disks, cluster administrators, failure domains, and backup systems. Separate clusters isolate the data plane more strongly; separate cloud accounts/projects can also isolate control plane, IAM, billing, network policy, and encryption-key administration. Each step increases cost and operational complexity.
| Boundary | Strengths | Residual shared risk | Operational cost |
|---|---|---|---|
| Tenant in key/row | Efficient pooling, simple capacity sharing | Predicate/routing bugs, noisy neighbors, shared backup/admin | Low |
| Collection/table | Namespace ACLs, easier per-tenant lifecycle | Shared cluster resources/control plane | Low–medium |
| Database | Stronger logical boundary and restore options in some products | Still shared cluster/node/ops | Medium |
| Cluster | Strong data-plane/failure isolation | May share cloud account, operators, networks | High |
| Account/project | Strongest IAM/network/billing/key boundary | Organization-level dependencies remain | Highest |
3. Routing keys, noisy neighbors, and tenant-specific recovery
A tenant key often becomes part of a partition or shard key. This can localize queries and simplify authorization, but a very large tenant can become a hot partition unless the key is further bucketed. Separate clusters reduce noisy-neighbor risk but can waste capacity for small tenants. Backup and restore are equally important: “database per tenant” is useful only if the chosen product can reliably back up and restore that unit with the required Recovery Point Objective (RPO), Recovery Time Objective (RTO), key access, and audit trail.
Encryption-key isolation can also differ. A shared data-encryption key across all tenants means a key compromise has a wider blast radius than per-tenant or per-domain keys, but per-tenant key lifecycle and rotation add significant operational state.
4. Deliberately wrong approach: filter by tenant only in the UI
If an API endpoint receives /orders/o-1 and
executes WHERE order_id='o-1' without tenant scope,
the database can correctly return records for multiple tenants.
A front-end tenant selector does not enforce authorization.
Likewise, a background job, search index, cache key, or export
can leak data if its key lacks tenant context.
The safer pattern is defense in depth: authenticated tenant context; authorization policy binding the identity to that tenant; tenant included in storage/routing keys; scoped database permissions where practical; and automated tests that intentionally try cross-tenant identifiers.
5. AtlasMart lab: leak one tenant, then make tenant context mandatory
Python 3.13+ standard library only. This models logical isolation and routing; no real tenant data, database, cloud account, or destructive test is used.
orders = [
{"tenant":"t-blue","order_id":"o-1","total":40},
{"tenant":"t-green","order_id":"o-1","total":900},
{"tenant":"t-blue","order_id":"o-2","total":75},
]
print("BROKEN QUERY: ORDER ID WITHOUT TENANT SCOPE")
def broken_get(order_id):
return [r for r in orders if r["order_id"] == order_id]
print("t-blue asks for o-1 but receives:", broken_get("o-1"))
print("\nREPAIRED TENANT-SCOPED ACCESS")
def get_order(tenant, order_id):
matches=[r for r in orders if r["tenant"]==tenant and r["order_id"]==order_id]
if len(matches) > 1: raise RuntimeError("duplicate tenant/order identity")
return matches[0] if matches else None
print("t-blue/o-1:", get_order("t-blue","o-1"))
print("t-green/o-1:", get_order("t-green","o-1"))
print("\nROUTING / ISOLATION OPTIONS")
options={
"shared-key": {"route":"cluster-A/shared-orders","restore":"filter by tenant","blast":"shared data plane"},
"table-or-collection": {"route":"cluster-A/tenant-namespace","restore":"namespace-level where supported","blast":"shared cluster"},
"database": {"route":"cluster-A/db-per-tenant","restore":"database-scoped where supported","blast":"shared cluster/control plane"},
"cluster": {"route":"cluster-per-tenant","restore":"cluster-scoped","blast":"stronger data-plane isolation"},
"account": {"route":"account/project-per-tenant","restore":"provider boundary","blast":"strongest admin/billing boundary, highest ops cost"},
}
for name,meta in options.items(): print(name, meta)
print("\nROUTER REQUIRES TENANT CONTEXT")
def route(ctx):
tenant=ctx.get("tenant")
if not tenant: raise PermissionError("tenant context required")
return f"partition-key={tenant}:order:{ctx['order_id']}"
try:
print(route({"order_id":"o-2"}))
except PermissionError as e:
print("DENIED:", e)
print("safe route:", route({"tenant":"t-blue","order_id":"o-2"}))
The broken lookup returns both blue and green tenant rows for
o-1. The repaired API requires tenant context and
composes it into the route/partition identity. This proves the
leak was an application authorization/routing failure—not a
database consistency failure.
6. Production judgment
Choose isolation from threat model, regulatory/contractual requirements, tenant size distribution, restore granularity, performance isolation, encryption-key policy, operational skill, and cost. Test cross-tenant access in APIs, background jobs, caches, secondary indexes, analytics exports, and support tooling. Observe per-tenant latency/saturation, quota breaches, hot partitions, authorization denials, backup scope, key scope, and administrative actions.
Do not promise “complete isolation” simply because tenants use separate databases; the same operators, credentials, network, backups, or cloud account may still join those environments. The next lesson follows tenant data into retention, legal-hold, audit, deletion, and backup-governance workflows.
Check your understanding
- Why is tenant_id only in the UI not a security boundary?
- What major tradeoff appears as isolation moves from shared keys to separate clusters/accounts?
- Why can one tenant per partition still create a performance problem?
- How does restore granularity influence tenant architecture?
- What should a cross-tenant test attempt?
Review the answers
1. Server-side authorization and storage/routing must enforce tenant scope; clients can be buggy or hostile.
2. Blast radius decreases, but infrastructure, capacity, automation, backup, monitoring, and lifecycle costs rise.
3. A very large or hot tenant can overload that partition; further bucketing or dedicated capacity may be required.
4. The isolation unit should align with what the product and runbook can back up, restore, validate, and audit within required RPO/RTO.
5. Use valid identifiers from another tenant across APIs, jobs, caches, indexes, exports, and admin tools and verify every path denies or scopes them correctly.
References
Foundational claims use standards, specifications, primary research, or current official documentation where practical. Product references are optional implementation anchors; the mandatory labs are vendor-neutral.
- NIST SP 800-53 Rev. 5 — Access-control, least-privilege, audit, cryptographic, and system-boundary control framework.
- OWASP Authorization Cheat Sheet — Server-side authorization and deny-by-default guidance relevant to tenant scoping.
- OWASP Multi-Tenant Security Cheat Sheet — Multi-tenant isolation risks and design/testing guidance.
- PostgreSQL 18 roles and privileges — Current role/privilege concepts useful when mapping tenant/database access boundaries.