Skip to content
Security First — Part 27 of 30

Supabase Security Hardening Checklist

Written by claude-sonnet-4 · Edited by claude-sonnet-4
supabasesecurityrlshardeningchecklistpostgres

Security First -- Part 27 of 30


When the Database Is the Front Door

Marcus had been building for three weeks. His AI-assisted project management app had real users, real data, and -- he thought -- real security. He had set up Supabase, wired up authentication, and deployed to Vercel. The anon key was in his frontend, which he had read was fine.

What Marcus had not done was enable Row Level Security on his tasks table.

On a Tuesday morning, a security researcher sent him a polite but alarming email. By hitting https://his-project.supabase.co/rest/v1/tasks?select=*, the researcher had pulled every task from every user in the database -- names, emails embedded in task descriptions, project details, client notes. No authentication required. One URL, thousands of records, zero friction.

Marcus is not alone. In September 2025, researchers at DeepStrike documented that hundreds to thousands of Supabase-backed applications were exposed this way -- including startups, medium-sized businesses, and large enterprises. The attack required no special skill: just knowledge of the predictable /rest/v1/<table> pattern that Supabase auto-generates.

A separate February 2026 case study found that a single misconfigured anon key led to the exposure of 28,000 customer records, Server-Side Request Forgery vulnerabilities, and complete account takeover capabilities. The root cause in every case: a misunderstanding of who is responsible for access control in the Supabase model.

That responsibility is yours. This checklist is how you meet it.


Why Supabase Security Is Different

In a traditional backend, your Express or Django server is the gatekeeper. Users never talk directly to your database. In Supabase, the database is the API. PostgREST generates REST endpoints directly from your PostgreSQL tables. That means your database's own access controls -- specifically Row Level Security -- are your backend security layer.

This is powerful when configured correctly. It is catastrophic when it is not.


The Supabase Security Hardening Checklist

Work through each item below. Most can be completed in under 15 minutes each.


1. Enable RLS on Every Table -- No Exceptions

By default, tables created via the SQL editor have RLS disabled. That means any request using your anon key can read every row.

-- Enable RLS on a table
ALTER TABLE public.tasks ENABLE ROW LEVEL SECURITY;

-- Add a policy so users only see their own rows
CREATE POLICY "Users can view own tasks"
  ON public.tasks FOR SELECT
  USING (user_id = auth.uid());

-- Also protect writes
CREATE POLICY "Users can insert own tasks"
  ON public.tasks FOR INSERT
  WITH CHECK (user_id = auth.uid());

Do this for every table. Use the Supabase Security Advisor (Dashboard > Reports > Security) to find tables with RLS disabled -- it runs check 0013 specifically for this.


2. Never Expose Your Service Role Key

Your project has two keys: the anon key and the service_role key. The service role key bypasses all RLS policies entirely -- it is a master credential with full read/write access to your entire database.

It should only ever live in server-side environment variables (a Vercel server function, an edge function, a backend API). If it appears in your frontend JavaScript bundle, your application is fully compromised.

Search your codebase:

grep -r "service_role" ./src

If you find it anywhere in client-side code, rotate it immediately in your Supabase dashboard and move it to a secret.


3. Understand What the Anon Key Actually Is

The anon key is safe to expose client-side -- but only when RLS is properly configured. It is the public key that unauthenticated visitors use to interact with your API. If your RLS policies are correct, the anon key can only access what you explicitly allow. If RLS is disabled or overly permissive, the anon key is a skeleton key to your data.

The anon key being public is not the vulnerability. Missing RLS is.


4. Harden Auth Configuration

In your Supabase Dashboard under Authentication > Providers > Email:

  • Enable email confirmation. Without it, an attacker can register with a fake email address and receive a valid JWT that satisfies auth.role() = 'authenticated' policies -- effectively forging an authenticated session. A March 2026 analysis on DEV Community called this the "Ghost Auth" flaw.
  • Set minimum password length to at least 8 characters (12 is better).
  • Enable rate limiting on auth endpoints. Supabase includes basic rate limiting by default, but review your thresholds under Authentication > Rate Limits.

5. Lock Down Storage Buckets

Storage buckets created as "public" serve files to anyone with the URL -- no authentication, no authorization. Audit your buckets under Storage in the dashboard.

For user-uploaded files, keep buckets private and generate signed URLs with expiration times:

-- Storage policy: users can only access their own files
CREATE POLICY "User file access"
  ON storage.objects FOR SELECT
  USING (auth.uid()::text = (storage.foldername(name))[1]);

6. Use Supabase Vault for Secrets in Edge Functions

If your Edge Functions connect to third-party APIs (Stripe, Resend, OpenAI), those credentials must not be hardcoded. Supabase Vault stores secrets encrypted at rest.

# Store a secret via the CLI
supabase secrets set STRIPE_SECRET_KEY=sk_live_...

In your edge function, access it via Deno.env.get('STRIPE_SECRET_KEY'). Never paste API keys directly into function code.


7. Apply Least Privilege to Database Roles

The anon and authenticated roles should only have access to what they genuinely need. Avoid blanket GRANT ALL statements. If you use the http extension for outbound requests, be especially careful: a September 2025 pentest finding showed that giving anon or authenticated roles EXECUTE on network-capable functions allowed Server-Side Request Forgery via /rest/v1/rpc/http_get.

-- Revoke unnecessary permissions
REVOKE EXECUTE ON FUNCTION public.http_get FROM anon, authenticated;

8. Restrict Direct Database Access

Your Supabase database has a direct connection string (port 5432) and a pooled connection via Supavisor (port 6543). For application code, always use Supavisor. For direct access, enable IP allowlisting in your project settings under Database > Network Restrictions. If your production server has a static IP, allowlist only that address. This prevents opportunistic port scanning from reaching your database.


9. RLS Applies to Realtime Subscriptions Too

If you use Supabase Realtime to listen for database changes, your RLS policies govern what rows the subscription can see. But this only works if you have RLS enabled and policies defined. A subscription to a table without RLS will stream all changes to all subscribers -- including changes to other users' rows.

Test your Realtime subscriptions the same way you test your REST endpoints: as a different user, verify you cannot see their data updates.


10. Enable Audit Logging

For applications handling personal data, financial records, or anything compliance-relevant, enable pgAudit to log who ran what queries. This is available on Pro and Enterprise plans. At minimum, review the logs available in your Supabase Dashboard under Logs > Database -- they capture slow queries, errors, and connection patterns.


11. Verify Your Backup Restore Process

As covered in Day 23, a backup you have never tested is not a backup. Supabase Pro includes daily backups with point-in-time recovery. Confirm your backup retention period in Database > Backups, and actually restore a backup to a staging project at least once to verify the process works before you need it.


12. Enable Two-Factor Authentication on Your Supabase Dashboard Account

Your Supabase dashboard has full access to your project settings, API keys, database, and secrets. If that account is compromised, everything else on this list becomes irrelevant. Enable 2FA under your account settings (avatar > Account > Security). Use an authenticator app, not SMS.


The Master Checklist

Copy this and mark each item complete:

  • RLS enabled on every table in the public schema
  • RLS policies use auth.uid() -- never trust IDs from request body
  • Service role key is server-side only, not in frontend code
  • Email confirmation enabled in Auth settings
  • Password minimum length configured
  • Rate limiting reviewed on auth endpoints
  • Storage buckets are private; signed URLs used for file access
  • Edge function secrets stored in Vault, not hardcoded
  • anon and authenticated roles audited for least privilege
  • Network restrictions configured; direct DB access restricted
  • Realtime subscriptions tested as a non-privileged user
  • Audit logging enabled or dashboard logs reviewed regularly
  • Backup restore process tested at least once
  • Two-factor authentication enabled on your Supabase account
  • Supabase Security Advisor run with zero critical findings

Ask The Guild

Share your experience in the community thread:

"Which item on this checklist surprised you most? Did the Security Advisor flag anything in your project that you had not noticed? Drop your findings -- and your RLS policy patterns -- in the comments."

The most common gap we see in vibe-coder projects is not the service role key. It is the table that got created three weeks into the project, when the developer was in a hurry, that never got RLS enabled. Run the Security Advisor. Find that table before someone else does.

Copy A Prompt Next

Start safely

If this article changed how you think about the problem, copy a prompt that turns that judgment into one safe, reviewable next step.

Matching public prompts

6

Keep the task scoped, copy the prompt, then inspect one reviewable diff before the agent continues.

Need the safest first move instead? Open the curated sample prompts before you browse the broader library.

Secrets HygieneStart Here — Build Safely With AI

Pre-Flight Secrets Check

Run this before you paste configs, screenshots, or terminal output into an AI tool so you do not leak API keys, connection strings, or internal URLs.

Preview
"I am about to share this small app with another person for the first time.
Please give me a beginner-safe pre-share review.
Context:
- project: [describe project]
- who will see it: [friend/coworker/client/internal team]
Security First

Turn this security lesson into a repeatable review habit

This article gives you the judgment call. The security paths give you the vocabulary, checklists, and repetition to catch the next issue before it reaches users.

Best Next Path

Security Essentials

Guild Member · $29/mo

Make the instincts in this article operational with concrete review checklists for secrets, auth boundaries, and common vulnerabilities.

28 lessonsIncluded with the full Guild Member library

Need the free route first?

Start with Start Here — Build Safely With AI if you want the workflow and vocabulary before you dive into the deeper path above.

T

About Tom Hundley

Tom Hundley writes for builders who need stronger technical judgment around AI-assisted software work. The Guild turns production experience into public articles, copy-paste prompts, and structured learning paths that help non-software developers supervise AI agents more safely.

Do this next

Leave this article with one concrete move. Copy the matching prompt, or start with the path that teaches the safest next skill in sequence.