[{"data":1,"prerenderedAt":323},["ShallowReactive",2],{"blog-stories/supabase-rls-missing":3},{"id":4,"title":5,"body":6,"category":302,"date":303,"dateModified":304,"description":305,"draft":306,"extension":307,"faq":308,"featured":306,"headerVariant":302,"image":308,"keywords":308,"meta":309,"navigation":310,"ogDescription":311,"ogTitle":308,"path":312,"readTime":313,"schemaOrg":314,"schemaType":315,"seo":316,"sitemap":317,"stem":318,"tags":319,"twitterCard":321,"__hash__":322},"blog/blog/stories/supabase-rls-missing.md","How Missing RLS Nearly Killed an Event Ticketing Startup",{"type":7,"value":8,"toc":280},"minimark",[9,16,21,24,27,33,36,40,43,46,49,55,58,61,65,70,73,76,87,91,94,97,101,105,108,114,117,121,124,127,137,141,144,147,151,155,158,162,165,169,172,181,185,188,216,244,268],[10,11,12],"tldr",{},[13,14,15],"p",{},"For 6 months, an event ticketing platform had no Row Level Security on Supabase. Any user could query any other user's data with basic browser dev tools. A technical customer discovered it during a demo. The team fixed it in hours, but the trust damage took months to repair. The startup nearly lost its biggest potential customer over it.",[17,18,20],"h2",{"id":19},"building-fast-securing-later","Building Fast, Securing Later",[13,22,23],{},"When the founder of an event ticketing platform started building the product, Supabase was the obvious choice because everyone said it was easy. And it was. Database in minutes. Auth in an hour. Features were shipping faster than ever.",[13,25,26],{},"The founder knew about Row Level Security. It was right there in the docs. But every time the team started to configure it, it felt complicated. The founder kept telling the team:",[28,29,30],"story-block",{},[13,31,32],{},"\"We'll add RLS when we have more users. Right now, it's just us and a few beta testers. Security can wait.\"",[13,34,35],{},"Six months later, the platform had 200 paying customers and RLS still hadn't been enabled.",[17,37,39],{"id":38},"the-demo-that-changed-everything","The Demo That Changed Everything",[13,41,42],{},"The team was doing a demo for a potential enterprise customer - a major concert venue chain. Big deal for them. The venue's technical lead was watching as the founder walked through the product.",[13,44,45],{},"About halfway through, the technical lead stopped the demo. \"Can I try something?\" she asked.",[13,47,48],{},"She opened browser dev tools, found a network request to Supabase, and modified the query. She removed the user ID filter.",[50,51,52],"danger-box",{},[13,53,54],{},"The response came back with data from every user in the system. Names. Emails. Their private event data. Everything.",[13,56,57],{},"The room went silent. She looked at the founder. \"You know this is a critical vulnerability, right?\"",[13,59,60],{},"The founder did know. The team just hadn't fixed it.",[17,62,64],{"id":63},"why-this-happened","Why This Happened",[66,67,69],"h3",{"id":68},"the-supabase-security-model","The Supabase Security Model",[13,71,72],{},"Supabase uses a different security model than traditional backends. Your anon key is public. It's embedded in your frontend code. Everyone can see it.",[13,74,75],{},"The security comes from Row Level Security policies. Without RLS, anyone with the anon key can query any data. And since the key is public, that means everyone.",[77,78,83],"pre",{"className":79,"code":81,"language":82},[80],"language-text","// Without RLS, this query returns ALL users\nconst { data } = await supabase\n  .from('projects')\n  .select('*')\n\n// Even if your app filters by user_id,\n// attackers can remove that filter\n","text",[84,85,81],"code",{"__ignoreMap":86},"",[66,88,90],{"id":89},"client-side-security-is-not-security","Client-Side Security Is Not Security",[13,92,93],{},"The app filtered data by user ID on the client side. The team thought that was enough. It's not.",[13,95,96],{},"Client-side code can be modified. Network requests can be intercepted and changed. If the server doesn't enforce access controls, the access controls don't exist.",[17,98,100],{"id":99},"the-aftermath","The Aftermath",[66,102,104],{"id":103},"the-immediate-fix","The Immediate Fix",[13,106,107],{},"The founder stayed up all night enabling RLS on every table. The actual fix wasn't complicated:",[77,109,112],{"className":110,"code":111,"language":82},[80],"-- Enable RLS\nALTER TABLE projects ENABLE ROW LEVEL SECURITY;\n\n-- Users can only see their own projects\nCREATE POLICY \"Users see own projects\"\n  ON projects FOR SELECT\n  USING (auth.uid() = user_id);\n\n-- Users can only modify their own projects\nCREATE POLICY \"Users modify own projects\"\n  ON projects FOR ALL\n  USING (auth.uid() = user_id);\n",[84,113,111],{"__ignoreMap":86},[13,115,116],{},"It took about 4 hours to properly secure all tables. Four hours of work that had been put off for six months.",[66,118,120],{"id":119},"the-enterprise-deal","The Enterprise Deal",[13,122,123],{},"The technical lead's report to her team was not favorable. They had concerns about the platform's security practices. The startup had to do a full security review, bring in an external auditor, and provide documentation of their security processes.",[13,125,126],{},"The deal almost fell through. The team only saved it by being completely transparent about what happened and what changed. The whole process took three months.",[128,129,130],"warning-box",{},[13,131,132,136],{},[133,134,135],"strong",{},"The hidden cost:"," That three-month delay in closing the deal cost the startup roughly $45,000 in potential revenue, not to mention the $8,000 spent on the security audit. The \"time saved\" by skipping RLS was negative.",[66,138,140],{"id":139},"notifying-existing-customers","Notifying Existing Customers",[13,142,143],{},"The team made the difficult decision to notify all customers about the vulnerability. Some were understanding. Others were furious. Two customers left immediately.",[13,145,146],{},"But most appreciated the honesty. The customer who found the issue? She became an advocate after seeing how the company handled it.",[17,148,150],{"id":149},"what-the-founder-learned","What the Founder Learned",[66,152,154],{"id":153},"security-is-product","Security Is Product",[13,156,157],{},"The founder used to think of security as separate from product work. Something to add later. Now the team understands that security IS the product. If the app isn't secure, it doesn't matter how many features it has.",[66,159,161],{"id":160},"rls-first-features-second","RLS First, Features Second",[13,163,164],{},"Now, whenever the team creates a new table in Supabase, the very first thing they do is enable RLS and write policies. Before any application code. It's part of the schema design, not an afterthought.",[66,166,168],{"id":167},"test-what-you-assume","Test What You Assume",[13,170,171],{},"The team assumed client-side filtering was sufficient. They never tested it. Now, they have automated tests that specifically try to access other users' data. If they succeed, the test fails.",[173,174,175],"lesson-box",{},[13,176,177,180],{},[133,178,179],{},"The expensive lesson:"," \"We'll add security later\" is a form of technical debt that compounds faster than feature debt. The cost of adding it later isn't just the development time. It's the potential data exposure, lost deals, damaged reputation, and customer trust that's incredibly hard to rebuild.",[17,182,184],{"id":183},"advice-for-other-founders","Advice for Other Founders",[13,186,187],{},"If you're using Supabase (or any similar platform), please learn from this startup's mistake:",[189,190,191,198,204,210],"ol",{},[192,193,194,197],"li",{},[133,195,196],{},"Enable RLS on day one."," Before you have users. Before you have data. Make it part of your initial setup.",[192,199,200,203],{},[133,201,202],{},"Test your security."," Try to access data you shouldn't be able to access. If you can, so can attackers.",[192,205,206,209],{},[133,207,208],{},"Understand the security model."," Supabase's security model is different from traditional backends. Read the docs. Understand how the anon key works.",[192,211,212,215],{},[133,213,214],{},"Don't trust client-side code."," Any security check that happens in the browser can be bypassed. Server-side enforcement is the only real security.",[217,218,219,226,232,238],"faq-section",{},[220,221,223],"faq-item",{"question":222},"What is Row Level Security (RLS)?",[13,224,225],{},"RLS is a PostgreSQL feature that lets you control which rows a user can access based on policies you define. In Supabase, RLS is essential because the anon key is public and embedded in your frontend code.",[220,227,229],{"question":228},"How do I know if my Supabase project is vulnerable?",[13,230,231],{},"Open browser dev tools, find a Supabase query, and try modifying it to remove user filters. If you can retrieve other users' data, RLS is missing or misconfigured. You can also check the Supabase dashboard to see which tables have RLS enabled.",[220,233,235],{"question":234},"Is the Supabase anon key supposed to be secret?",[13,236,237],{},"No. The anon key is designed to be public and embedded in frontend code. Security comes from RLS policies, not from hiding the key. Your service_role key, however, should never be exposed.",[220,239,241],{"question":240},"Can I use Supabase without RLS?",[13,242,243],{},"Technically yes, but only if you exclusively use the service_role key from a secure backend and never expose database access to the client. For most applications, especially those with a frontend, RLS is essential.",[245,246,247,253,258,263],"related-articles",{},[248,249],"related-card",{"description":250,"href":251,"title":252},"Another missing RLS story. AI-generated auth logic was literally backwards.","/blog/stories/lovable-app-exposed-18000-users","How a Lovable App Exposed 18,000 Users",[248,254],{"description":255,"href":256,"title":257},"Another RLS cautionary tale","/blog/stories/database-exposed","The Day My Database Was Exposed",[248,259],{"description":260,"href":261,"title":262},"Step-by-step guide","/blog/how-to/setup-supabase-rls","How to Set Up Supabase RLS",[248,264],{"description":265,"href":266,"title":267},"Complete security checklist","/blog/checklists/supabase-security-checklist","Supabase Security Checklist",[269,270,273,277],"cta-box",{"href":271,"label":272},"/","Start Free Scan",[17,274,276],{"id":275},"is-your-database-exposed","Is Your Database Exposed?",[13,278,279],{},"Scan your Supabase project for missing RLS and other vulnerabilities.",{"title":86,"searchDepth":281,"depth":281,"links":282},2,[283,284,285,290,295,300,301],{"id":19,"depth":281,"text":20},{"id":38,"depth":281,"text":39},{"id":63,"depth":281,"text":64,"children":286},[287,289],{"id":68,"depth":288,"text":69},3,{"id":89,"depth":288,"text":90},{"id":99,"depth":281,"text":100,"children":291},[292,293,294],{"id":103,"depth":288,"text":104},{"id":119,"depth":288,"text":120},{"id":139,"depth":288,"text":140},{"id":149,"depth":281,"text":150,"children":296},[297,298,299],{"id":153,"depth":288,"text":154},{"id":160,"depth":288,"text":161},{"id":167,"depth":288,"text":168},{"id":183,"depth":281,"text":184},{"id":275,"depth":281,"text":276},"stories","2026-02-24","2026-03-06","An event ticketing platform founder discovers their Supabase database had no Row Level Security. Any user could see any other user's data. The story of discovery, panic, and recovery.",false,"md",null,{},true,"An event ticketing platform founder discovers their Supabase database had no Row Level Security for 6 months.","/blog/stories/supabase-rls-missing","9 min read","[object Object]","BlogPosting",{"title":5,"description":305},{"loc":312},"blog/stories/supabase-rls-missing",[320],"Supabase RLS","summary_large_image","3DhBmis1QsBenskp2EFEDNLw5Z7sKiyBl2OE3uv5-Qw",1775843936129]