Row-Level Security (RLS)
A database feature that enforces, at the database itself, which rows each user or tenant can see or change, so isolation holds even if application code has bugs.
Why it matters
Most data leaks between customers in shared systems come from one missed WHERE clause. RLS matters because it moves tenant isolation from 'every developer remembers, every time' to 'the database refuses, always'. For any SaaS or multi-branch system holding other people's data, it converts a class of catastrophic bugs into non-events.
How it works
Policies are declared on tables: for example, a row is visible only when its tenant_id matches the requesting session's tenant. The database evaluates the policy on every query, application mistakes cannot bypass it because the enforcement happens below the application. Forced RLS extends this so even the table owner's connections obey policies.
A real-world example
A boutique-management platform serves hundreds of shops from one database. Each row carries the shop's id and RLS policies bind every query to the signed-in shop. A developer later ships a buggy report query with no shop filter; instead of leaking every shop's sales, the database returns only the caller's rows. The bug becomes a wrong report, not a breach.
Common mistakes
- ✗ Enforcing isolation only in application code and hoping every query remembers
- ✗ Enabling RLS but leaving service-role connections that bypass it in normal request paths
- ✗ Writing policies against client-supplied values instead of authenticated session context
- ✗ Never testing cross-tenant access as part of CI
Best practices
- ✓ Put a tenant id on every multi-tenant table and FORCE row-level security on
- ✓ Derive policy inputs from verified session claims, never from request parameters
- ✓ Keep privileged bypass connections out of user-facing paths and audit their use
- ✓ Add automated tests that attempt cross-tenant reads and expect failure
Frequently asked questions
Does RLS replace application authorization?
No, it backstops it. RBAC decides what actions a role may take; RLS guarantees which rows those actions can ever touch. Together they give defence in depth.
Is RLS slow?
Policies add a predicate to queries; with proper indexes on tenant columns the overhead is typically negligible compared to the risk it removes.
Which databases support it?
PostgreSQL has mature support (widely used via platforms like Supabase); SQL Server and others offer equivalents. The concept transfers even where syntax differs.
AEGIBIT Cortex enforces tenant isolation with PostgreSQL RLS on every multi-tenant table, a tenant id on every row, checked by the database on every query.
How Cortex isolates tenants →Related concepts