Cookbook recipe

Row-Level Security Bypass

Applies to PostgreSQL 13–17 Last reviewed May 2026 Grounded in source
Estimated investigation4 min

Scenario

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…

Investigation Path

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.

This is a Pro lesson

Get every Learning Pathway and cookbook recipe — grounded in PostgreSQL source code, with diagnostics, fixes, and prevention for each topic.

Continue this lesson to learn:

  • Mitigation Actions
  • All 36 Learning Pathway lessons
  • 170+ cookbook recipes
  • Source-grounded diagnostics & fixes

Secure checkout Cancel anytime Source-grounded

Career Impact

This scenario builds production judgment and operational confidence under pressure.

Open Career Dashboard →

Keep going

Related & next steps

Was this helpful?

← All cookbook recipes