What Is This Vulnerability
The Supabase anonymous (anon) key is a public API key designed to be used in client-side code. It is intentionally safe to expose only when Row Level Security (RLS) policies are correctly configured on every table. Anonymous key misuse occurs when developers rely on the anon key for API access but fail to implement adequate RLS policies, leaving tables accessible to anyone who possesses the key.
Unlike a secret key, the anon key carries the anon role in the JWT payload. If a table has RLS disabled or has overly permissive policies, any HTTP request bearing the anon key can read or modify data without authentication.
Why It's Dangerous
The anon key is embedded in every client bundle. Attackers can trivially extract it from browser DevTools, mobile binaries, or bundled JavaScript. When RLS is missing or misconfigured, this key becomes an all-access pass to your database.
Consequences include:
- Full table reads — sensitive user data, billing records, and private content become publicly queryable.
- Unauthorized writes — attackers can insert, update, or delete rows in unprotected tables.
- Privilege escalation — if role-related tables lack RLS, attackers can elevate their own permissions.
Even a single unprotected table can compromise the entire application.
How to Detect
Query your database to find tables without RLS enabled:
SELECT schemaname, tablename, rowsecurity
FROM pg_tables
WHERE schemaname = 'public'
AND rowsecurity = false;
You can also test from the client side using the anon key:
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(SUPABASE_URL, ANON_KEY);
// Attempt to read a table that should be restricted
const { data, error } = await supabase.from('user_profiles').select('*');
if (data && data.length > 0) {
console.warn('WARNING: user_profiles is accessible with anon key');
}
AuditYour.app automatically checks every public table for RLS status and tests whether the anon key can read or write data without authentication.
How to Fix
Enable RLS on every public table and create restrictive policies:
-- Step 1: Enable RLS
ALTER TABLE user_profiles ENABLE ROW LEVEL SECURITY;
-- Step 2: Create a policy that restricts reads to authenticated owners
CREATE POLICY "Users read own profile"
ON user_profiles FOR SELECT
USING (auth.uid() = id);
-- Step 3: Create a policy that restricts writes to authenticated owners
CREATE POLICY "Users update own profile"
ON user_profiles FOR UPDATE
USING (auth.uid() = id)
WITH CHECK (auth.uid() = id);
-- Step 4: Deny anonymous access explicitly if needed
CREATE POLICY "Deny anon access"
ON user_profiles FOR ALL
USING (auth.role() != 'anon');
Audit all tables regularly. Use automated scanning to catch newly created tables that lack RLS before they reach production.
Scan your app for this vulnerability
AuditYourApp automatically detects security misconfigurations in Supabase and Firebase projects. Get actionable remediation in minutes.
Run Free ScanRelated
vulnerabilities
Missing Row Level Security Policy
Tables without RLS are fully exposed to any user with the anon key, allowing unrestricted read and write access to all rows.
vulnerabilities
Service Role Key Exposure
The Supabase service_role key is exposed in client-side code, granting full database access that bypasses all RLS policies.