Skip to content

Cybersecurity Engineering Handbook / Chapter 17

Multi-Tenancy and Isolation

Design and prove tenant isolation across data, compute, identity, logs, backups, support tooling, and AI retrieval paths.

The refund path is reachable by design. Now put two customers in the same service. An operator from Northstar may approve Northstar’s refund; an operator from Red Fern may approve Red Fern’s. Both use the same route, workers, caches, search service, logs, and support console. The hard question is no longer whether the path is allowed. It is whether every stage of that allowed path preserves the customer boundary.

A check at the HTTP handler cannot carry that promise alone. The request may produce a queue message, a cached result, an export, a search document, a log event, and a backup. Support may later inspect it, and an AI assistant may retrieve it. Tenant isolation is the invariant that all of those copies and operations must preserve. The design is credible only when the team can show where tenant context comes from, where it is enforced, and how a cross-tenant attempt fails at each stage.

Tenant isolation comparison chart showing shared database, separate schema, separate database, separate account or project, and dedicated deployment across control strength, cost, operational burden, and blast radius.
Tenant isolation is a trade-off between control strength, cost, operational burden, and blast radius. The selected model must be explicit before engineers write queries, caches, jobs, exports, and support tools.

Choose what is shared before defending it

Isolation is a spectrum, and one system may use several points on it. A shared application and database place the most weight on application and data-access controls: every row, object, cache entry, job, and index record must retain the right tenant. Separate schemas strengthen the data boundary but make routing, migrations, analytics, and restores tenant-aware. Separate databases reduce data blast radius further while multiplying provisioning, connection, backup, and failover work.

Cloud accounts, projects, or subscriptions can add IAM, network, quota, and logging boundaries. A dedicated deployment separates still more of the stack, at the cost of fleet management, coordinated releases, and incident response across many installations. Dedicated infrastructure is not self-proving: a shared support plane, identity provider, log store, backup account, deployment service, or analytics pipeline can quietly reconnect tenants above it.

Choose the model from the consequences of failure. Data sensitivity, contractual promises, regulatory duties, adversarial tenants, noisy-neighbor risk, recovery objectives, and the feasible operating burden all belong in the decision. Price may reflect that decision; it should not make the decision by accident. Record which resources remain shared and how their blast radius is bounded.

Establish tenant context once, verify it repeatedly

The Northstar operator may send an organization slug, tenant header, or refund identifier, but none of those values establishes authority. The server first authenticates the person, resolves the selected account through a membership record, and produces a canonical tenant identifier. If the person belongs to several tenants, switching the active tenant is an explicit, logged state change. A client-supplied tenant value is useful as a request for context, not as proof of context.

The authorization decision then binds three facts: the actor, the active tenant, and the requested object or operation. Checking that the actor has the refund-approver role is insufficient; that role must apply in Northstar, and the refund must also belong to Northstar. Looking up a refund by object ID and checking tenant ownership afterward creates unnecessary risk. Prefer an interface in which the tenant is part of the lookup itself:

approve_refund(
  actor = operator-482,
  tenant = tenant-northstar,
  refund = refund-91,
  authority = refund.approve
)

repository lookup:
  tenant_id = tenant-northstar AND refund_id = refund-91

Tenant-bound repositories, database row policies, separate credentials, and policy libraries can make an unscoped query difficult or impossible to express. Use more than one enforcement layer when the consequence justifies it. Application policy can reject the wrong action, while a data-layer policy still prevents a missed predicate from becoming disclosure. Neither layer excuses the other.

Services downstream must acquire the same authenticated context or derive it again from trustworthy state. A tenant header copied through an internal network is still merely a header. Protect the service channel, bind tenant claims to the calling workload and intended audience, and enforce them at each resource boundary. An earlier service’s success is evidence about that hop, not permanent authority for the rest of the chain.

Follow the invariant beyond the request

Suppose the approval handler writes the refund and enqueues settlement work. The message needs one unambiguous tenant and an object identifier whose ownership the worker verifies. A batch containing records from several tenants needs explicit per-record context or must be split; ambient worker state must not decide which customer’s credentials, database, or destination to use. Job retries and dead-letter handling must retain the same boundary.

Caching introduces another authority decision. A key such as refund:refund-91 can return a perfectly valid Red Fern response to a Northstar request. Include the tenant in the namespace and include any policy-relevant subject, role, or data classification when two callers in the same tenant should see different representations. On retrieval, do not treat a matching key as a substitute for validating the cached object’s tenant. Flush, warming, and invalidation tools need the same scope as reads.

Search, analytics, and AI retrieval create derived copies with their own failure modes. Carry tenant metadata during indexing, require a tenant filter that cannot be empty or bypassed, and verify the tenant again before returning source content. If the retrieval component builds the filter from prompt text, it has surrendered the boundary to untrusted input. Tool calls made by an AI assistant must execute with the requesting tenant’s authority, not a global service credential, and citations must point only to sources the caller may read. Separate indexes or services are warranted when a shared filter is too fragile for the promised level of isolation.

Exports deserve end-to-end review because they move data out of the guarded request path. Scope the query, generated file, object-store location, encryption, recipient, download authorization, expiry, and deletion to one tenant. A tenant-correct query can still leak through a predictable filename, shared bucket listing, broad email destination, or support-accessible export history.

Logs should carry a stable tenant identifier so responders can reconstruct the boundary, but should not copy sensitive tenant content merely to make investigation convenient. Log viewers, traces, metrics labels, and analytics jobs need scoped access too. Observability is a data path, not an exemption.

Backups preserve whatever isolation properties their restore procedure can recreate. Know whether recovery happens by row, schema, database, account, or whole deployment. Test restoring Northstar without overwriting Red Fern, mixing records, exposing another tenant’s keys, or placing recovered data in a location with broader access. If the platform only supports a fleet-wide restore, document the temporary environment, access boundary, extraction method, verification, and destruction of surplus restored data.

Separate operations as carefully as data

Tenant identity also shapes compute, network, keys, and human administration. Per-tenant rate limits and quotas prevent one customer from exhausting shared workers, queues, connections, indexes, storage, or provider budgets. Reserve or separate capacity where a shared pool cannot meet the availability promise. Suspension should stop the abusive tenant’s work without disabling unrelated customers, while preserving the evidence needed to investigate it.

Network segmentation can reduce the resources a tenant workload can reach, especially for separate accounts or dedicated deployments. It cannot replace object and tenant authorization inside a shared service. Encryption keys may be shared at a service boundary, separated by tenant, or customer-managed; the choice must agree with the threat model and contract. Where keys are separated, key selection, grants, rotation, recovery, and deletion must all be driven by verified tenant context. A tenant-specific key used through a global decrypt credential offers less separation than its label suggests.

Support tooling is often the shortest route around product controls. Do not give a global administrator an invisible bypass. A support session should name the operator, customer, reason, approved scope, permitted actions, start and end time, and evidence. Impersonation must be conspicuous to the operator, revocable, and distinguishable from the customer’s own actions. High-impact access may require separate approval and customer notification. Emergency access still needs a narrow boundary and retrospective review.

These controls also define notification boundaries. Logs and incident tooling must answer which tenants and data classes were affected without granting every responder unrestricted access to all customer data.

Prove both the permission and the denial

A successful Northstar refund proves that one intended path works. Isolation needs the neighboring failures. Build a test fixture with at least two tenants, similar object shapes, overlapping human roles, and deliberately confusing identifiers. Then exercise the boundary at the enforcement layer, not only through a mocked controller.

The core test set should show that:

  • a Northstar approver can act on a Northstar refund;
  • a Northstar member with the wrong role cannot approve it;
  • that approver cannot retrieve a Red Fern refund by guessing its object ID, changing a tenant parameter, or replaying a cursor;
  • a forged tenant header or claim does not change server-resolved membership;
  • a job with missing or mixed tenant context is rejected or safely split;
  • cache keys, invalidation, and warming cannot collide across tenants;
  • search and vector retrieval fail closed when the tenant filter is absent, malformed, or contradicted by retrieved metadata;
  • an export cannot be listed, downloaded, or delivered by another tenant;
  • a tenant-scoped restore cannot mix, overwrite, or expose another tenant’s records or keys; and
  • support impersonation requires the expected approval, scope, audit trail, expiry, and revocation.

Fuzz tenant IDs, object IDs, pagination cursors, sort and filter fields, batch payloads, and alternate content types. Review raw queries as well as API responses. Secondary paths—counts, autocomplete, errors, exports, scheduled reports, and cleanup jobs—often reveal existence or content after the main detail endpoint has been secured.

Keep a compact isolation record for each consequential path:

operation: approve and settle refund
tenant source: authenticated membership -> canonical tenant id
shared resources: API, queue, workers, cache, database, logs
enforcement: policy check; tenant-bound repository; scoped worker credential
derived copies: cache entry, job payload, audit event, export
negative proof: foreign object, forged context, cache collision, mixed job
operations: tenant quota, suspension, support session, incident owner
recovery: tenant-scoped restore test and key selection check
residual risk: shared database and worker pool; bounded by policy, RLS, and quotas

This worksheet is valuable only when it names actual enforcement and evidence. “Tenant-aware” is a claim, not an implementation.

Respond to a boundary crossing without widening it

If a cross-tenant exposure is suspected, first disable or narrow the smallest suspect route, job, export, support function, or retrieval path. Preserve request IDs, policy decisions, query logs, cache entries, job payloads, exports, and access records under restricted incident access. Do not destroy evidence because it contains customer data.

Scope the event by tenant, object, actor or workload, data class, enforcement point, and time window. Check adjacent paths that reuse the same repository, cache namespace, index, credential, or support privilege. Correct the control at the layer that failed, add a regression test there, and run the relevant negative set before restoring service. Continue watching for attempted crossings after the fix.

Customer, contractual, legal, and regulatory communications follow the approved incident process; engineering must provide the facts that make those decisions possible. Notify only from evidence, but do not let uncertainty about the complete blast radius delay containment or preservation.

The review is finished when the team can trace one tenant’s authority through every stored and derived copy, demonstrate the corresponding denials, contain one tenant without disabling the rest, and state honestly where shared infrastructure still couples their fate. Cloud architecture can then place those boundaries in accounts, networks, identities, and guardrails without mistaking resource layout for proof of isolation.