What is Row Level Security?
Supabase exposes your PostgreSQL tables as a REST API through PostgREST. That API is live the moment you create a table. The only thing standing between the public internet and your data is RLS — a set of policies that tell PostgreSQL "this user can only see rows where user_id matches their session."
Without RLS enabled, the anon role has full read access to every row in the table. No authentication required. Anyone with your Supabase URL and anon key — both of which are public by design — can query the entire table.
RLS isn't a Supabase-specific concept. It's built into PostgreSQL itself and has been available since PostgreSQL 9.5 (2016). Supabase just made it the centerpiece of their security model. If you skip it, you've skipped the entire access control layer.
How does Row Level Security work?
When you create a table in Supabase, RLS is disabled by default. The table is immediately accessible through the REST API to anyone with the anon key. You need two things to lock it down: enable RLS on the table, and create at least one policy.
Here's what a typical AI-generated table looks like versus the fixed version:
-- AI-generated migration: creates table, skips RLS CREATE TABLE profiles ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES auth.users(id), full_name TEXT, email TEXT, phone TEXT ); -- No ALTER TABLE ... ENABLE ROW LEVEL SECURITY -- No CREATE POLICY -- Result: every row is public via the REST API
CREATE TABLE profiles ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES auth.users(id), full_name TEXT, email TEXT, phone TEXT ); -- Lock the table down ALTER TABLE profiles ENABLE ROW LEVEL SECURITY; -- Users can only read their own profile CREATE POLICY "Users read own profile" ON profiles FOR SELECT USING (auth.uid() = user_id); -- Users can only update their own profile CREATE POLICY "Users update own profile" ON profiles FOR UPDATE USING (auth.uid() = user_id);
Why do AI tools generate Row Level Security vulnerabilities?
AI code generators are great at creating database schemas. They define tables, set up foreign keys, and even add indexes. But they almost never enable RLS or write policies — because the prompt didn't ask for them, and the app works fine without them during development.
- RLS is a security layer, not a functional requirement. "Create a profiles table with name and email" produces a working table. RLS doesn't change the app's behavior — it restricts it. The model skips what wasn't requested.
- The anon key makes it invisible. During development, everything works because you're the only user. The anon key has full access, and there's no second user to trigger a conflict. The bug only surfaces in production.
- Policies require auth context the model doesn't have. Writing a correct RLS policy means knowing which column maps to the authenticated user. That's app-specific logic the model can't infer from a schema prompt.
The Supabase docs explicitly warn: "If you don't turn on RLS, any client with the anon key can read and modify all data." But AI tools generate the SQL, not the docs page. The warning never reaches the builder.
Common Row Level Security patterns
Table created with no RLS statement
The migration has CREATE TABLE but no ALTER TABLE ... ENABLE ROW LEVEL SECURITY. Every row is publicly readable.
RLS enabled but no policies defined
RLS is on, which blocks all access by default — but then the app breaks, and the builder disables it instead of writing a policy.
Overly permissive policy
A policy like USING (true) or FOR ALL USING (true) WITH CHECK (true) passes every check. RLS is on in name only.
Service role key used client-side
The service_role key bypasses RLS entirely. If it's exposed in client-side code, RLS policies are irrelevant.
How Flowpatrol detects Row Level Security
Flowpatrol checks your Supabase-backed app for missing or misconfigured RLS the way an attacker would — from the outside, using only what's publicly available:
- 1Identifies Supabase endpoints by detecting the REST API URL and anon key in your app's client-side code.
- 2Queries tables with the anon key to see which ones return data without authentication — the exact test for missing RLS.
- 3Tests cross-user access by authenticating as one user and attempting to read or modify rows belonging to another.
- 4Reports exposed tables with the specific rows leaked, the query used, and the exact ALTER TABLE and CREATE POLICY statements to fix it.
Missing RLS is the single most common vulnerability in Supabase apps built with AI tools. Flowpatrol catches it in seconds.
Related terms
Check your Supabase tables for missing RLS.
Flowpatrol detects exposed tables and missing policies in your Supabase app. Paste your URL and find out what's public.
Try it free