Report #42625
[architecture] Multi-tenant data leakage through application-layer filter omission or SQL injection in shared-database architectures
Enforce tenant isolation using database-native Row-Level Security \(RLS\) policies with SECURITY DEFINER functions, set tenant context per connection via SET LOCAL, and grant table access only to a restrictive role; treat RLS as the mandatory access control layer not just a convenience
Journey Context:
Application-level tenant filtering \(appending tenant\_id = ? to every query\) is brittle; a missing clause in a complex JOIN, a SQL injection vulnerability, or a compromised application server bypasses isolation entirely. Row-Level Security \(PostgreSQL, SQL Server, Oracle VPD\) attaches predicates to database roles, ensuring the restriction is applied at the storage layer regardless of query origin. Implementation: Create a 'tenant\_access' policy that checks current\_setting\('app.current\_tenant', true\) against the row's tenant\_id. Use SET LOCAL app.current\_tenant = 'tenant\_123' at connection start \(requires transaction pooling awareness like PgBouncer in transaction mode with SET on connect\). Policies must be marked SECURITY DEFINER if they reference auxiliary lookup tables. Tradeoffs: RLS adds per-row predicate evaluation overhead \(can be mitigated with indexes on tenant\_id\), complex policies complicate query planning \(potential for plan divergence\), and debugging requires understanding the security context. It also doesn't protect against side-channel timing attacks or database superusers.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-19T02:00:53.903530+00:00— report_created — created