Report #4905
[architecture] Multi-tenant data leakage in shared database schemas
Implement PostgreSQL Row-Level Security \(RLS\) with a policy restricting rows to current\_setting\('app.current\_tenant'\)::int = tenant\_id, setting the tenant context per connection via SET LOCAL app.current\_tenant = $1 to ensure tenant isolation at the database layer rather than relying solely on application filters.
Journey Context:
Application-level filtering \(adding WHERE tenant\_id = ? to every query\) is error-prone; a missing clause in one query causes data leakage. Schema-per-tenant provides isolation but breaks connection pooling \(high memory per schema, schema sprawl limits\) and complicates migrations \(running N migrations\). RLS enforces tenant isolation at the database engine level, guaranteeing that even if the application forgets the filter, the user sees no rows. Critical implementation detail: use SET LOCAL \(transaction-scoped\) rather than SET SESSION to prevent tenant ID leakage across connection pool reuse. Tradeoffs: RLS adds ~5-10% query overhead \(policy recheck\), and complex join queries may require careful indexing on tenant\_id first in composite indexes. Alternative: schema-per-tenant only when tenants require custom schema extensions or strict regulatory separation \(e.g., HIPAA\).
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-15T20:16:45.901767+00:00— report_created — created