HOW TO SECURE SUPABASE - SECURITY GUIDE | VIBEEVAL
Critical: RLS is Required
Without Row Level Security enabled, anyone with your public anon key can read, modify, or delete every row in your database. The anon key is in your frontend bundle by design — what makes Supabase safe is the policy, not the key. RLS is not optional; it is the foundation of Supabase security.
Security Checklist
1. Enable RLS on every public table
ALTER TABLE my_table ENABLE ROW LEVEL SECURITY; — or in the dashboard: Database → Tables → [table] → Enable RLS. With RLS enabled but no policies, all access is blocked — you must add a policy in the same change to keep the app working. Verify with the Supabase RLS Checker.
2. Write specific RLS policies per operation
Don’t write USING (true) — that defeats the point. Per operation:
CREATE POLICY "users read own rows" ON profiles
FOR SELECT USING (auth.uid() = user_id);
CREATE POLICY "users insert own rows" ON profiles
FOR INSERT WITH CHECK (auth.uid() = user_id);
CREATE POLICY "users update own rows" ON profiles
FOR UPDATE USING (auth.uid() = user_id) WITH CHECK (auth.uid() = user_id);
Note USING (read filter) and WITH CHECK (write predicate) are both required for UPDATE — missing WITH CHECK lets users move rows to other users.
3. Test RLS policies with real keys
Don’t trust the dashboard’s “Policies look correct.” Test by querying with the anon key and a real Authorization: Bearer <jwt> header for two different test users. Confirm: user A sees only A’s rows, user B sees only B’s rows, neither can write to the other’s. The Supabase RLS Checker automates this.
4. Protect the service_role key
The service_role key bypasses RLS entirely. Never embed it in frontend code, never expose it via VITE_* / NEXT_PUBLIC_* / PUBLIC_* env vars, never commit it to git. Use it only in server-side functions / Edge Functions / your own backend. If exposed: rotate immediately at Settings → API → Reset service_role secret, then audit logs for the exposure window.
5. Understand anon-key safety
The anon key is meant to be public. With RLS configured, an anon key gives users access only to what your policies allow. Without RLS, the same key gives anyone everything. Search your bundle for the key string only as a sanity check — the real audit is the policy set.
6. Configure Supabase Auth correctly
Authentication → Providers → Email: enable “Confirm email” so users must verify before login. Authentication → Sign In/Up → Password requirements: minimum 8 chars + complexity. Authentication → URL Configuration → Site URL: set to your production domain only (no *). Authentication → Sessions: set inactivity timeout and absolute lifetime to your tolerance.
7. Configure Storage policies per bucket
Storage → [bucket] → Policies: same model as table RLS — enable + write a policy. Recurring shape: INSERT WITH CHECK (auth.uid()::text = (storage.foldername(name))[1]) to scope uploads to a per-user folder. Without a policy, the bucket is publicly writable.
8. Require SSL on database connections
In Settings → Database → Connection pooling: confirm SSL is required. For direct Postgres connections, append ?sslmode=require to the URL. For Supavisor-pooled connections, SSL is on by default — verify in the connection string.
9. Use database roles for service-side access
For your own server / function code that needs to bypass RLS for legitimate admin tasks: don’t use the global service_role; create a custom role with the minimum schema-level permissions and grant only the operations needed (GRANT SELECT, INSERT ON specific_table TO admin_role). Reduces blast radius if any individual key leaks.
10. Audit Edge Functions
Every Edge Function: confirm authentication is checked (Deno.env.get('SUPABASE_ANON_KEY') plus a verified JWT), inputs are validated, secrets come from Deno.env.get not hardcoded. The function’s response goes back to a client — don’t include internal state, file paths, or stack traces in errors.
11. Enable rate limiting on Auth endpoints
Authentication → Rate Limits: configure per-IP limits on /token, /signup, /recover, /verify. Defaults are generous; tighten to 10/min on auth endpoints. Without this, the auth endpoints are a free credential-stuffing surface.
12. Audit Realtime subscriptions
Database → Replication: only enable replication on tables that need realtime. Realtime subscriptions enforce RLS — but only if the publication includes the row. Per-row authorization is your responsibility through RLS; don’t enable replication on a table you haven’t policy’d.
13. Configure Point-in-Time Recovery
Settings → Database → Point in Time Recovery: enable for production projects (Pro plan and up). PITR is the only way to recover from a “user accidentally deleted everything” or “RLS misconfiguration deleted half the table” incident.
14. Enable audit logging
For Pro+ projects: Settings → Logs → Database: review weekly for: bulk reads from the anon key, repeated 4xx auth errors, queries to tables you don’t expect frontend traffic on. Anomalies are usually a sign someone is probing.
15. Run an automated RLS scan
The Supabase RLS Checker probes every public table and reports which return rows without auth — catches the “RLS forgotten on a new table” regression that ships every time AI adds a new feature. Schedule it as a CI step on every deploy.
Common Vulnerabilities in Supabase Apps
Tables Without RLS
The default for newly-created tables is RLS off. AI tools (Lovable, Bolt, Cursor) frequently create tables mid-conversation and don’t enable RLS — see the RLS misconfiguration atlas.
USING (true) Policies
Enabling RLS but writing USING (true) is the same as no RLS. Common when an AI tool “fixes” a missing-RLS warning by adding a permissive policy.
Self-Editable Role Fields
A profiles table with an is_admin column and an UPDATE policy that allows any field — the user PATCHes {is_admin: true} and is now admin. Strip role-related columns from the policy or use a trigger.
service_role Key in Frontend
A VITE_SUPABASE_SERVICE_ROLE_KEY env var ships to the browser. Bypasses every policy. Rotate immediately on discovery.
Related Resources
Supabase RLS Misconfiguration Atlas
The recurring patterns we see across AI-built Supabase apps.
Free Self-Audit Suite
Five free scanners.
Vibe Coding Security Risk Guide
Full risk catalogue.
Test Your RLS Policies Automatically
VibeEval probes every public Supabase table with the anon key and reports which return data — and where the policy logic has gaps. Findings ship with paste-ready SQL fixes.
SCAN YOUR APP
14-day trial. No card. Results in under 60 seconds.