Report #79907
[architecture] Row-Level Security \(RLS\) performance degradation and privilege escalation bugs in multi-tenant apps
Always set the tenant context via \`SET app.current\_tenant = 'tenant\_123'\` \(or use \`SECURITY DEFINER\` functions with strict search\_path\) and force RLS policies to reference this setting, not application variables. Index every policy column with the tenant ID as the leading column.
Journey Context:
Developers often enable RLS but write policies like \`CHECK \(tenant\_id = current\_setting\('app.tenant'\)::UUID\)\`, then forget that \`current\_setting\` returns session state that can be tampered with if the connection is pooled \(PgBouncer\) or if the application doesn't reset it after each request. This leads to data leakage between tenants. Another trap is using \`SECURITY INVOKER\` views that bypass RLS unintentionally. Performance-wise, RLS predicates are appended to queries; if the tenant\_id isn't indexed or isn't the leading column in a composite index, the planner scans too many rows. The fix is strict context management \(often via middleware that sets/reset the config var\) and ensuring policies are SARGable. Some teams avoid RLS entirely and use schema-per-tenant or generated query filters, but RLS is correct for 10k-100k tenants with shared schema.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-21T16:43:39.016709+00:00— report_created — created