What You'll Learn
45% of AI-generated code has at least one security vulnerability (Stanford study). That doesn't mean you can't ship — it means you need to check before you ship.
Why AI Code Has Security Issues
AI tools optimize for "does it work" — not "is it secure." When you ask for a login page, the AI builds one that logs you in. Whether it stores passwords safely, protects against injection attacks, or hides API keys is secondary.
The training data includes millions of code examples, and a huge portion of those examples have security issues. The AI copies patterns, including the insecure ones. It doesn't know the difference.
Warning: Code that runs without errors is not the same as code that's safe to deploy. A working app with exposed API keys is a ticking time bomb.
The 10-Point Security Checklist
Run through this before you ship anything to real users:
console.log statements with sensitive dataPro tip: Print this list. Tape it next to your screen. Check it every time you deploy. It takes 10 minutes and prevents disasters.
Supabase RLS — What It Is and Why It Matters
Row Level Security (RLS) controls who can read and write each row in your database. Without it, any authenticated user can see and modify every other user's data.
Think of it this way: without RLS, your database is a shared Google Sheet where everyone can see everyone else's rows. With RLS, each person only sees their own rows.
What happens without RLS: User A signs up. User B signs up. User B opens browser dev tools, changes a request, and reads all of User A's data. Name, email, private notes — everything.
Here's a simple RLS policy that restricts users to their own data:
-- Enable RLS on the table ALTER TABLE todos ENABLE ROW LEVEL SECURITY; -- Users can only see their own todos CREATE POLICY "Users can view own todos" ON todos FOR SELECT USING (auth.uid() = user_id); -- Users can only insert their own todos CREATE POLICY "Users can insert own todos" ON todos FOR INSERT WITH CHECK (auth.uid() = user_id); -- Users can only update their own todos CREATE POLICY "Users can update own todos" ON todos FOR UPDATE USING (auth.uid() = user_id); -- Users can only delete their own todos CREATE POLICY "Users can delete own todos" ON todos FOR DELETE USING (auth.uid() = user_id);
Warning: If you're using Supabase and haven't set up RLS, stop reading this guide and do it now. This is the #1 security issue in vibe-coded apps.
API Keys — Where They Go
Simple rule: if a key gives access to data or costs money, it goes on the server. Never the client.
Correct — server-side only:
# .env.local (never committed to git) OPENAI_API_KEY=sk-abc123... STRIPE_SECRET_KEY=sk_live_... SUPABASE_SERVICE_ROLE_KEY=eyJ...
Incorrect — exposed to every visitor:
# These are visible in the browser NEXT_PUBLIC_OPENAI_KEY=sk-abc123... NEXT_PUBLIC_STRIPE_SECRET=sk_live_...
In Next.js, any variable starting with NEXT_PUBLIC_ is bundled into the frontend and visible to anyone who opens your site. Use it only for values that are meant to be public (like your Supabase anon key or a Google Analytics ID).
Never commit .env files to Git. Add .env and .env.local to your .gitignore. If you already committed one, the keys are in your Git history and should be rotated immediately.
What to Check in Your Browser Console
Open your deployed app. Press F12 (or right-click → Inspect). Go to the Network tab. Click around your app.
Look at each request. Expand the headers and the request URL. If you see any of these, you have a problem:
?key=sk-abc123)Try this: Open your app right now, open DevTools, and click through every feature. If you see any secret in the Network tab, that key is exposed to every visitor on your site.
The One Prompt to Run Before Shipping
Copy this prompt and run it in Cursor with your full codebase open:
Review this entire codebase for security vulnerabilities. Check for: - Exposed API keys or secrets in frontend code - Missing authentication checks on protected routes - SQL injection or XSS vulnerabilities - Supabase tables without Row Level Security - Sensitive data in console.log statements - Hardcoded credentials List every issue found with the file name, line number, and how to fix it.
Pro tip: Run this prompt in Cursor with your full codebase open. It catches things you'd never think to look for. Run it after every major feature addition, not just before launch.
This isn't a replacement for a professional security audit. But for most vibe-coded MVPs and side projects, it catches the critical issues that would otherwise ship to production.
Want an automated check? Try our Security Checker tool.
Built by Us
This guide is based on real builds. gptsters.com is built with vibe coding — see for yourself.