How to Secure a Supabase App: The Developer's Guide
Supabase is the default backend for vibe coders. Build with Lovable? You're on Supabase. Using Base44? Supabase. Even Bolt and Cursor projects frequently use Supabase for auth and data.
The problem is that Supabase gives you the keys to a powerful database — and AI tools don't know how to lock the doors.
This guide covers the 10 most critical Supabase security mistakes and exactly how to fix each one.
Why Supabase Security Matters
Supabase is a thin layer on top of PostgreSQL. Unlike Firebase (which has its own security rules language), Supabase security relies on Row Level Security (RLS) — a PostgreSQL feature most developers have never used.
When AI generates your Supabase schema, it creates tables, inserts data, and connects your frontend. What it usually skips:
- Enabling RLS
- Creating access policies
- Separating
anonvsservice_rolekey usage - Securing Edge Functions
The result: your entire database is readable (and often writable) by anyone who opens their browser's developer tools.
The 10 Most Common Supabase Security Mistakes
1. RLS Not Enabled
Severity: Critical
This is the #1 security issue in Supabase apps. If RLS is not enabled on a table, the anon key provides full access.
Check: Go to Supabase Dashboard → Table Editor → Click any table → Check the RLS toggle.
Fix:
-- Enable RLS on every table
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
-- IMPORTANT: RLS is enabled but no policies = nobody can access
-- You must create policies after enabling RLS
2. No RLS Policies Defined
Enabling RLS without creating policies denies all access — which breaks your app. The AI then either disables RLS (bad) or creates overly permissive policies (also bad).
Common bad policy:
-- DON'T DO THIS — allows everyone to read all data
CREATE POLICY "Allow all" ON profiles FOR SELECT USING (true);
Correct policies:
-- Users can only read their own profile
CREATE POLICY "Users read own profile" ON profiles
FOR SELECT USING (auth.uid() = id);
-- Users can only update their own profile
CREATE POLICY "Users update own profile" ON profiles
FOR UPDATE USING (auth.uid() = id);
-- Users can insert their own profile
CREATE POLICY "Users insert own profile" ON profiles
FOR INSERT WITH CHECK (auth.uid() = id);
3. Service Role Key in Frontend Code
Supabase gives you two keys:
anonkey — Safe for frontend use, respects RLSservice_rolekey — Bypasses RLS completely, admin access
AI code generators frequently use the service_role key in frontend code because it "just works" (no RLS issues). This is catastrophic.
Check: Search your codebase for service_role or look for the key that starts differently from your anon key in your client-side code.
Fix: Only use service_role in server-side code (Edge Functions, API routes). Use anon key for all frontend operations.
4. Missing Auth Verification in Edge Functions
Supabase Edge Functions are serverless functions that run on Deno. AI often generates them without verifying the user's JWT token.
Insecure:
Deno.serve(async (req) => {
const { data } = await supabase.from('users').select('*');
return new Response(JSON.stringify(data));
});
Secure:
Deno.serve(async (req) => {
const authHeader = req.headers.get('Authorization');
if (!authHeader) {
return new Response('Unauthorized', { status: 401 });
}
const { data: { user }, error } = await supabase.auth.getUser(
authHeader.replace('Bearer ', '')
);
if (error || !user) {
return new Response('Unauthorized', { status: 401 });
}
// Now fetch data scoped to this user
const { data } = await supabase
.from('users')
.select('*')
.eq('id', user.id);
return new Response(JSON.stringify(data));
});
5. Storage Buckets Without Policies
Supabase Storage also uses RLS-style policies. AI-generated apps often create public buckets for profile images, documents, or uploads — making every file accessible to anyone.
Fix:
-- Only authenticated users can upload to their own folder
CREATE POLICY "Users upload own files" ON storage.objects
FOR INSERT WITH CHECK (
bucket_id = 'avatars' AND
auth.uid()::text = (storage.foldername(name))[1]
);
-- Only authenticated users can view their own files
CREATE POLICY "Users view own files" ON storage.objects
FOR SELECT USING (
bucket_id = 'avatars' AND
auth.uid()::text = (storage.foldername(name))[1]
);
6. Email Confirmations Disabled
For a smoother development experience, Supabase lets you disable email confirmation. AI-generated apps often ship to production with this still disabled, allowing:
- Fake account creation
- Email enumeration attacks
- Account takeover via unverified emails
Fix: Supabase Dashboard → Authentication → Settings → Enable "Confirm email."
7. Weak Password Requirements
Supabase's default minimum password length is 6 characters. AI tools don't increase this.
Fix: Supabase Dashboard → Authentication → Settings → Set minimum password length to at least 8, ideally 12. Require at least one uppercase, lowercase, number, and special character.
8. No Database Backups Configured
This isn't a vulnerability per se, but if your app gets compromised and data is deleted, you need backups.
Fix: Enable Point-in-Time Recovery (PITR) in your Supabase project settings. Available on Pro plan and above.
9. Exposed Supabase Project URL
Your Supabase project URL (e.g., https://abc123.supabase.co) is technically public, but AI-generated code sometimes exposes the full REST API URL pattern, making it easier for attackers to enumerate your tables.
Fix: Use API Gateway or custom domain to proxy Supabase requests. At minimum, ensure RLS is properly configured on every table.
10. No Webhook Verification
If you're using Supabase webhooks (for Stripe, email providers, etc.), AI-generated code often skips signature verification — meaning anyone can send fake webhook events to your app.
Fix: Always verify the webhook signature before processing the payload.
How Proveably Scans Supabase Apps
Proveably has built-in Supabase detection and scanning:
- Detects Supabase projects from your app's JavaScript bundle
- Checks RLS status via API probing
- Scans for exposed keys in source code and source maps
- Tests storage bucket permissions
- Validates auth configuration for common misconfigurations
All results come with AI-generated fix suggestions specific to your Supabase setup.
Supabase Security Checklist
| Check | Status |
|---|---|
| RLS enabled on every table | ☐ |
| Policies created for every table | ☐ |
service_role key only in server-side code |
☐ |
| Edge Functions verify JWT tokens | ☐ |
| Storage buckets have access policies | ☐ |
| Email confirmation enabled | ☐ |
| Password requirements strengthened | ☐ |
| Database backups configured | ☐ |
| Webhook signatures verified | ☐ |
| Regular security scans running | ☐ |
Scan Your Supabase App Now
Don't guess — scan. Paste your URL into Proveably and find out exactly what's exposed in 30 seconds.
Related reading: