IS SUPABASE SAFE? SUPABASE SECURITY REVIEW 2026 | VIBEEVAL
RLS is Non-Negotiable
Supabase exposes your PostgreSQL database directly to clients via the anon key. Without RLS policies, anyone with your project URL can read, modify, or delete all data in unprotected tables. The anon key is not a secret — it ships in the JavaScript bundle of every Supabase app on purpose. That design choice is fine when RLS is enforced. When RLS is not enforced, every table is a public API.
The pattern we see most often: AI generators scaffold a Supabase project, create five tables, and write the frontend that reads from them. They never run ALTER TABLE x ENABLE ROW LEVEL SECURITY. The app works in dev because the dev project has one user. It works in prod for one user. It works for two users — until user B types user A’s UUID into the URL bar.
Common Security Issues
Missing RLS Policies
Tables without RLS enabled are fully accessible to anyone with the anon key, leading to complete data exposure. Check with:
SELECT schemaname, tablename, rowsecurity
FROM pg_tables
WHERE schemaname = 'public' AND rowsecurity = false;
Anything in that result is publicly readable through the Supabase REST or GraphQL endpoint.
Service Role Key Leaks
The service_role key bypasses RLS. Exposing it in client code grants full database access to attackers. Common leak paths: pasted into a .env.local that ships in the Next.js bundle (NEXT_PUBLIC_* variables), committed to a public repo “just for the demo”, logged at server boot, or returned in an error response from an Edge Function.
Rotate immediately if you suspect a leak — and after rotation, audit pg_stat_statements for queries that wouldn’t have come from your app.
Flawed RLS Policies
RLS policies with logical errors create unintended access paths. The most common mistake is the “permissive policy that allows everything”:
-- BAD: this is the same as no RLS
CREATE POLICY "users can read" ON profiles
FOR SELECT USING (true);
-- GOOD: scope to the authenticated user
CREATE POLICY "users read own profile" ON profiles
FOR SELECT USING (auth.uid() = user_id);
Equally dangerous is the policy that uses auth.uid() only on SELECT and forgets UPDATE/DELETE/INSERT. Each command needs its own policy unless you use FOR ALL.
Storage Bucket Misconfigurations
Supabase Storage also requires RLS. Public buckets may expose sensitive files. The standard mistake is making a bucket public so the CDN works, then storing per-tenant files in it with predictable paths like /uploads/{user_id}/{file}.pdf. Anyone who guesses a UUID gets the file.
For private buckets, write a Storage policy:
CREATE POLICY "users read own files"
ON storage.objects FOR SELECT
USING (auth.uid()::text = (storage.foldername(name))[1]);
JWT Secret Reuse Across Projects
Supabase signs JWTs with a per-project secret. If you copy the secret across staging and prod (or share it across two unrelated apps), a token issued in one environment is valid in the other. Always treat the JWT secret as a per-environment value.
Unprotected Database Functions
SECURITY DEFINER functions run with the privileges of the function owner — usually a superuser. A function that takes a user_id parameter and reads from any table without checking the caller is a complete RLS bypass. Audit every SECURITY DEFINER function and confirm it validates auth.uid() against its inputs.
Security Assessment
Strengths
-
- PostgreSQL with enterprise-grade security
-
- Row Level Security (RLS) for fine-grained access
-
- Built-in authentication with JWT tokens
-
- Open source - security auditable
-
- SOC 2 Type II compliance
-
- Encryption at rest and in transit by default
-
- Per-project JWT signing keys
Concerns
-
- RLS policies often missing or misconfigured
-
- Default settings may expose data
-
- Anon key in client code - RLS is essential
-
- Service role key leaks grant full access
-
- Complex RLS syntax leads to security gaps
-
- SECURITY DEFINER functions can bypass RLS silently
-
- Storage buckets need their own policies, easy to forget
Supabase vs Firebase: a quick model comparison
Both platforms put a database directly behind a public key and rely on declarative rules to keep data scoped. The differences:
- Rule language. Supabase uses SQL via
CREATE POLICY. Firebase uses its own Security Rules DSL. SQL is more powerful and composable; the DSL is more compact for simple per-document rules. - Default after enable. RLS enabled with no policy = deny all. Firebase rules with no
matchblock = deny all. Both fail safely if you stop halfway. Both fail unsafely if you write a permissive rule to “make it work”. - Auth integration. Both ship first-party auth that injects a user ID into rules. Supabase exposes
auth.uid(); Firebase exposesrequest.auth.uid. - Server escape hatch. Supabase has
service_role. Firebase has the Admin SDK. Both bypass all rules. Both are the source of most catastrophic leaks.
The Verdict
Supabase is safe as a platform with PostgreSQL’s battle-tested security. The critical factor is proper RLS configuration. Enable RLS on every table, write and test policies thoroughly, and never expose the service_role key in client code. With proper configuration, Supabase provides excellent security.
The four checks before production:
- Every table in the
publicschema hasrowsecurity = true. - Every table has at least one policy per command (
SELECT,INSERT,UPDATE,DELETE) that referencesauth.uid(). - The service_role key exists only in trusted server environments — grep your repo and your client bundle for it.
- Storage buckets are private unless they are genuinely meant for the public CDN, and private buckets have policies that scope by user.
Related Resources
How to Secure Supabase
Step-by-step security guide covering RLS rollout, policy testing patterns, and Storage bucket lockdown.
Supabase Security Checklist
Interactive security checklist with the SQL queries to confirm each item.
Supabase RLS Checker
Run an automated scan against your live Supabase project that probes every table for RLS bypass and reports any rows the anon key can reach.
Token Leak Checker
Find the service_role key (and other secrets) that may have shipped to your browser bundle.
Scan Your Supabase App
Let VibeEval check your Supabase application for RLS misconfigurations and vulnerabilities. The scanner walks every public route, exercises the anon key against your tables, and reports the rows it should not have been able to read.
COMMON QUESTIONS
SCAN YOUR APP
14-day trial. No card. Results in under 60 seconds.