Partition Cache Keys by Tenant
Check that cache keys cannot return one tenant's data to another.
A cache can erase a correct database boundary. The first request may query Tenant A with proper authorization, then store the response under a key that names only the route and record ID. A later Tenant B request can receive the cached value without reaching the database. OWASP Multi Tenant Security includes shared caches among the resources that need isolation.
Audit cache identity
Inventory each cache that holds tenant-derived data: in-process maps, Redis, query caches, edge caches, and generated page caches. Record the inputs to each key and the trust source for those inputs. A sound key usually needs the tenant plus every permission-relevant dimension, or the cached object must be public to all callers.
| Cache entry | Key dimensions to review | Invalidation event |
|---|---|---|
| Record detail | Tenant, record, permitted view | Update, delete, access change |
| Search result | Tenant, query, role or policy version | Index update, role change |
| Export status | Tenant, job, requester | Completion, expiry, revocation |
| Member list | Tenant, role | Invitation or membership change |
Use two synthetic tenants with different marker values. Warm the cache as A, then request the same route or overlapping ID as B. Reverse the order. Repeat after a role change and deletion. The test must compare actual response bytes and cache-hit evidence, not just the application status. Also check that a cache miss does not leak another tenant through a fallback key.
A tenant key derived from an untrusted header can be changed by the caller. Derive it from verified identity context. If cache entries vary by role, include policy state or avoid caching private responses. Invalidation is part of the security rule; a former member must not receive a response cached while they were authorized.
Check invalidation
Illustrative private record key: tenant=A | principal=U1 | record=R7 | view=billing | grantVersion=12. Check U1's current billing permission before any cache hit; derive key dimensions from verified server state. Warm R7 as U1, then request it as U2 in the same tenant with the same grant version but no billing grant. U2 must receive no billing bytes. Revoke U1's grant and retry with the old browser state; the fresh permission check must deny access even if U1's old entry still exists. Record the cache hit or bypass and invalidation outcome. A shared entry is acceptable only if its key and fresh check represent a well-defined effective permission identity, not merely a tenant and version.
AWS tenant isolation guidance describes pooled resources that depend on policy. A shared cache is one such resource. The artifact here is a key-and-invalidation table. It gives a reviewer a testable design instead of the weak claim that “the database is tenant-aware.”
Find the gaps in your access controls
See how an illustrative cross-workspace finding connects evidence, impact and a fix. Discuss the tenant boundaries your team needs reviewed.