Three RLS patterns we've found in real production apps that silently expose user data across accounts.

Row Level Security is one of the best features Supabase offers. It lets you define access rules directly at the database level, so even if your application code has a bug, the database itself refuses to return data a user should not see. But RLS is only effective when configured correctly, and we consistently find three patterns in production SaaS apps where RLS is either absent, bypassed, or misconfigured. Each of these mistakes can expose every row in a table to any authenticated user, and in some cases, to unauthenticated visitors.

02Mistake 1: RLS Not Enabled at All

When you create a new table in the Supabase dashboard, RLS is disabled by default. This means the table is fully accessible to anyone with the anon key, which is a public key embedded in your frontend code. We regularly scan apps where the founders added several tables during development, enabled RLS on the main users table, but forgot to enable it on related tables like orders, messages, or uploads. An attacker who inspects your frontend JavaScript can find the anon key and the Supabase URL, construct a direct API call, and read every row in any unprotected table. The fix is simple: enable RLS on every table immediately after creation, even before you write your first policy.

03Mistake 2: Overly Broad Policies

A common pattern is to write a SELECT policy like "allow all authenticated users to read all rows" with the expression (auth.role() = 'authenticated'). This satisfies the requirement of "RLS is enabled" but does nothing to limit cross-user access. User A can read User B's data freely. The correct policy for most tables is (auth.uid() = user_id), which restricts reads to rows where the user_id column matches the authenticated user. Make sure this column exists on every table that holds user-specific data and that it is set correctly on insert (either by a default value or a trigger, not by client-side code that can be spoofed).

04Mistake 3: Service Role Key Exposed

Supabase provides two keys: the anon key (safe for the browser) and the service role key (bypasses all RLS). If the service role key is exposed, every RLS policy you have written is irrelevant. We find service role keys in frontend bundles, in public git repositories, and in .env files committed to GitHub history. The service role key should only exist in server-side environment variables, never prefixed with NEXT_PUBLIC_, and never committed to version control. Audit your git history with a tool like trufflehog or a Guardian scan to confirm it has never been exposed. If it has, rotate the key immediately in the Supabase dashboard.

The Guardian Team
Security for apps built with AI.

Scan your Supabase RLS policies

Guardian detects missing RLS, overly permissive policies, and exposed service role keys automatically.

Scan my app
More articles