Scenario
A multi-tenant SaaS application uses Row-Level Security on the customer_data table to restrict each user to their own tenant’s rows. The policy is enabled and works for regular users. However, the analytics team (using the analytics_role) sees all rows from all tenants. The DBA checks and finds analytics_role has BYPASSRLS = true — granted during initial setup for reporting purposes, never reviewed.
How to Identify
Conditions:
pg_policies shows RLS enabled but certain role sees all rows
pg_roles.rolbypassrls = true for the affected role
- Table created as owner and not running
FORCE ROW LEVEL SECURITY
SECURITY DEFINER function is used that bypasses the caller’s RLS policy
Analysis Steps
-- Check which roles have BYPASSRLS
SELECT rolname, rolbypassrls, rolsuper
FROM pg_roles
WHERE rolbypassrls = true OR rolsuper = true
ORDER BY rolname;
-- Both rolsuper=true and rolbypassrls=true bypass RLS
-- Check RLS status on tables
SELECT tablename, rowsecurity, forcerowsecurity
FROM pg_tables
WHERE schemaname = 'public'
AND rowsecurity = true
ORDER BY tablename;
-- rowsecurity=true but forcerowsecurity=false = table owner bypasses RLS
-- Check existing RLS policies
SELECT schemaname, tablename, policyname, roles, cmd, qual, with_check
FROM pg_policies
WHERE tablename = 'customer_data';
-- Test: what does the analytics role actually see?
-- SET ROLE analytics_role;
-- SELECT count(*) FROM customer_data; -- all rows or only own tenant?
-- RESET ROLE;
-- Check if table owner bypasses RLS:
SELECT t.tablename, r.rolname AS owner, r.rolsuper
FROM pg_tables t
JOIN pg_roles r ON t.tableowner = r.rolname
WHERE t.tablename = 'customer_data';
-- Table owner with rolsuper=true bypasses RLS regardless of policy
Pitfalls
- Table owners bypass RLS by default, even with
ROW SECURITY enabled. Use ALTER TABLE ... FORCE ROW LEVEL SECURITY to apply RLS to owners too.
SUPERUSER bypasses RLS unconditionally — FORCE ROW LEVEL SECURITY does NOT apply to superusers.
BYPASSRLS is a role attribute that completely skips RLS for that role on all tables in the database.
SECURITY DEFINER functions run as the function owner. If the owner has BYPASSRLS or is the table owner, the function sees all rows — the caller’s RLS context is ignored.
- RLS policies are evaluated at query time — complex policies with current_user comparisons require the session’s
SET ROLE to match the intended role.
Resolution Approach
Revoke BYPASSRLS from roles that don’t need it. Apply FORCE ROW LEVEL SECURITY to sensitive tables so even table owners are restricted. Review SECURITY DEFINER functions that access RLS-protected tables.