
๐ค Ghostwritten by Claude Opus 4.6 ยท Curated by Tom Hundley
If you're building with Supabase and you haven't turned on Row Level Security (RLS), your entire database is wide open to the internet right now. Not "at risk." Not "potentially vulnerable." Wide open. Anyone who finds your Supabase project URL and anon key โ both of which are embedded in your frontend code โ can read, modify, or delete every single row in every table that doesn't have RLS enabled. Supabase Row Level Security is the feature that controls who gets to see and change which rows in your database. Think of it as a bouncer at each table who checks IDs before letting anyone touch the data. Without it, there's no bouncer. The door is propped open. Everyone walks in.
This isn't theoretical. It happened โ at massive scale โ just recently. And it'll happen to you if you don't spend the next ten minutes with me.
TL;DR: A vibe-coded app called Moltbook shipped without Supabase RLS, exposing 1.5 million API tokens, 30,000 email addresses, and private messages to anyone who knew where to look.
Here's the story. Moltbook was an app built quickly โ likely with AI coding tools โ and deployed on Supabase. The developers never enabled RLS on their database tables. Security researchers at Wiz flagged it as part of a broader investigation into AI-prototyped apps. What they found was staggering: 1.5 million API tokens, over 30,000 user emails, and private messages sitting in database tables that anyone could query directly.
No hacking required. No sophisticated exploit. Someone just had to use the Supabase JavaScript client with the publicly visible anon key and run a simple query. That's it. Every user's data, handed over on a silver platter.
Wiz specifically called this out as a pattern they're seeing with AI-built apps: developers use tools like Cursor, Bolt, or Replit to scaffold a Supabase backend, and the AI generates working code โ but doesn't enable RLS. The app works perfectly in testing. It looks great. But the database has no access controls whatsoever.
According to research published by GitClear in 2024, AI-assisted code contains roughly 23.7% more security issues than code written manually. Missing Supabase database security configurations like RLS are one of the most common gaps.
TL;DR: RLS is a set of rules you attach to each database table that determines which users can see, add, change, or delete which rows โ without it, every row is visible to everyone.
Let me explain Supabase Row Level Security without any jargon.
Imagine you have a giant filing cabinet in the lobby of a public building. Each drawer is a table in your database (users, messages, orders). Each folder inside is a row โ one user's data, one message, one order.
Without RLS: The filing cabinet is unlocked and sitting in the lobby. Anyone who walks in can open any drawer, read any folder, photocopy it, shred it, or stuff new folders in. That's your Supabase database right now if RLS is off.
With RLS: You hire a security guard who sits next to the cabinet. Every time someone reaches for a drawer, the guard checks their ID badge and a rulebook. The rulebook says things like:
That rulebook? Those are your RLS policies. The guard? That's Supabase's built-in policy engine.
Here's the critical thing: Supabase ships with RLS disabled on new tables by default. When your AI coding tool creates a new table, it almost certainly doesn't turn RLS on. Your filing cabinet is sitting unlocked in the lobby from the moment you create it.
| Scenario | Without RLS | With RLS |
|---|---|---|
| User queries another user's data | โ Returns all data | โ Returns nothing (blocked) |
| Anonymous visitor reads your tables | โ Can read everything | โ Blocked unless you allow it |
| Someone tries to delete all rows | โ Deletes everything | โ Blocked by policy |
| Your app works normally for logged-in users | โ Works | โ Works (policies allow their own data) |
TL;DR: If you've built anything on Supabase with Cursor, Bolt, Replit, Lovable, or v0, you need to check your RLS settings immediately โ your users' data may already be exposed.
Let me be blunt. If you've shipped a Supabase app and you don't specifically remember enabling RLS and writing policies, your data is probably exposed. Not "might be." Probably is.
Your Supabase anon key is not a secret. It's embedded in your frontend JavaScript. Anyone can find it by opening browser developer tools on your site. That key is designed to be public โ Supabase expects RLS to be the actual security layer. The anon key is like a library card that gets you in the building. RLS is the lock on the restricted archives.
If you've been storing user emails, passwords, messages, payment info, health data, or anything personal โ and RLS is off โ you may already have a legal obligation to disclose this. I'm not trying to scare you for fun. I'm trying to get you to act today.
This also connects to API key security โ your Supabase anon key being public is fine only if RLS is properly configured. And if you're storing service role keys in your frontend code instead of environment variables, you've got an even bigger problem.
TL;DR: Go to your Supabase dashboard, check every table for RLS status, enable it, and add at least a basic policy โ here are the exact steps.
If you see "RLS is not enabled โ any user can read and modify data in this table" โ that's the red alert.
After enabling RLS, you need at least one policy per table or your app will stop working (because now nobody can access anything โ the guard is blocking everyone because there's no rulebook yet).
For a typical table where users should only see their own rows:
auth.uid() (the logged-in user's ID) to a user_id column in your tableThe policy looks something like:
CREATE POLICY "Users can view own data"
ON your_table
FOR SELECT
USING (auth.uid() = user_id);You don't need to write this SQL yourself โ the dashboard has templates. But you DO need to make sure your tables have a user_id column that matches the user's auth ID.
Paste this into Cursor, Bolt, Replit, or whatever AI tool you're using:
"Review every Supabase table in this project. For each table: (1) confirm that Row Level Security (RLS) is enabled, (2) show me the existing RLS policies, (3) if RLS is not enabled or there are no policies, generate the SQL to enable RLS and create policies that restrict SELECT, INSERT, UPDATE, and DELETE operations so users can only access rows where the user_id column matches auth.uid(). Use a default-deny approach where no access is allowed without an explicit policy. Show me the complete SQL I need to run in the Supabase SQL Editor."
This prompt forces your AI assistant to audit your Supabase database security and generate the exact RLS policies you need. Don't just trust the output โ read through it and make sure each table has appropriate rules.
Supabase Row Level Security (RLS) is a database feature that controls which users can see or modify which rows in your tables. Vibe coders need it because Supabase's anon API key is public by default โ it's embedded in your frontend code and visible to anyone. Without RLS enabled, that public key gives anyone full access to read, modify, or delete every row in your database. RLS is the actual security barrier that makes your public API key safe to use.
Yes, temporarily โ and that's by design. When you enable RLS without adding any policies, Supabase blocks ALL access to that table. This is called "default-deny" and it's the safest starting point. You then add specific policies that grant access only to the right users for the right operations. Your app will work again once you add the appropriate policies. It's better to have a briefly broken app than a permanently exposed database.
Log into your Supabase dashboard, click Table Editor in the left sidebar, and click on each table. Look for the RLS status indicator at the top of the table view. You can also go to Authentication > Policies to see all your tables and their RLS status in one place. Any table showing "RLS disabled" is fully exposed to anyone with your project URL.
Most AI coding tools like Cursor, Bolt, and Replit do not enable RLS automatically when creating Supabase tables. This is one of the most dangerous gaps in AI coding for Supabase RLS configuration. You should always explicitly prompt your AI tool to enable RLS and create appropriate policies for every table it creates. Use the prompt provided in this article to audit your existing project.
The anon key is meant to be public โ it's used in your frontend code and identifies your project. It relies entirely on RLS policies to restrict what users can do. The service role key bypasses all RLS policies and gives full admin access to your database. The service role key must NEVER appear in frontend code โ only in server-side environments or environment variables. If someone gets your service role key, RLS won't protect you.
RLS is the foundation, but there's more to Supabase API key protection than flipping a switch. Tomorrow we're going to talk about the difference between client-side and server-side security โ and why some operations should never happen in the browser.
If today's lesson made your stomach drop because you realized your tables are exposed, take a breath. Go fix it right now. It takes ten minutes. The hardest part is already done โ you know the problem exists.
Share this with anyone you know who's building with Supabase. Especially if they're using AI tools to code. They probably need to hear this.
You've got this. See you tomorrow.
โ Tom
Discover more content: