Operations, Security & Reliability

Row-Level Security and Data Boundaries

Row-level security adds a database-enforced predicate to ordinary table access, but safe tenant isolation also requires trustworthy identity propagation, constrained ownership and bypass roles, complete policy coverage, and adversarial tests.
  • RLS is defense in depth, not tenant identity discovery.
  • Visibility and writes need separate reasoning.
  • Owners and privileged roles may bypass policy.
  • Policy composition changes meaning.
  • RLS does not solve every side channel.
  • A denied-case suite is mandatory.
PostgreSQL: tenant policy on the canonical orders table
-- This multi-tenant example extends the canonical
-- commerce.orders(order_id, customer_id, placed_at, status) with tenant_id.
ALTER TABLE commerce.orders ENABLE ROW LEVEL SECURITY;
ALTER TABLE commerce.orders FORCE ROW LEVEL SECURITY;

CREATE POLICY orders_tenant_access ON commerce.orders
  AS PERMISSIVE FOR ALL TO orders_runtime
  USING (tenant_id = current_setting('app.tenant_id', true)::uuid)
  WITH CHECK (tenant_id = current_setting('app.tenant_id', true)::uuid);

CREATE POLICY orders_not_archived ON commerce.orders
  AS RESTRICTIVE FOR SELECT TO orders_runtime
  USING (status <> 'ARCHIVED');
Policy tests
CaseExpected
Tenant A selects A orderrow returned
Tenant A selects/updates/deletes B orderzero rows or denied
Tenant A inserts/retags row as BWITH CHECK rejects
Identity absent or malformedfail closed; transaction discarded/reset
Table owner or BYPASSRLS roleexplicitly demonstrates bypass
Pooled connection reused for Btransaction-local identity reset; only B visible

Set tenant context from authenticated server-side data inside the transaction, not from raw request text. Prefer a transaction-local setting and revoke direct table access from roles that should enter through a trusted routine or view. The exact mechanism is vendor-specific; the invariant is a non-forgeable identity-to-session binding.