Course B · Module B1 Production · Lesson 9 of 18
A handful of silent mistakes account for most vibe-coded app breaches. Learn to spot them before you ship.
Security feels abstract until your database gets wiped, your API key runs up a $4,000 bill, or a stranger reads every customer's private data. The uncomfortable truth about AI-generated apps is that the AI writes functional code — but it rarely writes defensive code unprompted. It doesn't know that your database rules let anyone read everything, or that an API key is sitting in a public file. You have to check. Your single win: by the end you'll know the four silent vulnerabilities that catch vibe-coded apps most often, and you'll have a concrete pre-launch checklist to work through before your app goes live.
These four problems are "silent" because the app works perfectly in development — nothing crashes, no warning appears. The damage only shows up when someone finds and exploits them.
An API key checked into a public GitHub repo, or hardcoded into client-side JavaScript, is visible to anyone — and bots scan GitHub constantly. Within minutes of a key appearing publicly, it can be used to rack up charges, send spam, or exfiltrate data in your name. See B5 for the right pattern: environment variables only, never in code.
Most AI-scaffolded projects spin up a database where any authenticated — or worse, unauthenticated — request can read or write any row. Your customer's orders, messages, and profile data are visible to every other customer. Supabase calls these Row Level Security (RLS) policies; Firebase has Security Rules. They default to open. You must turn them on.
When your app takes text from a user and passes it directly into a database query or an AI prompt without any checks, an attacker can craft input that does something completely different — deleting data, extracting records, or hijacking the AI's instructions. The fix is usually small: validate and sanitize input on the server side, and use your database library's parameterized queries instead of raw string concatenation. Ask your AI to review every place user input touches the database or an API call.
It's common to build an admin panel quickly — a page to view all users, delete records, send test emails — and forget to protect it. If the route exists and isn't behind an auth check, it's publicly accessible to anyone who guesses or finds the URL. Check every internal route: does it verify that the requester is logged in and has the right role?
Think of your app as a building. Authentication (B6) is the front door lock. Database access rules are the internal doors — between the lobby and the server room, between one tenant's office and another's. An API key is the master key to the whole building. Trusting user input blindly is like a staff member who follows any instruction written on a sticky note, even one left by a stranger.
You can have a great front door and still be wide open inside. Security is layers, not a single lock.
Work through this before every launch — or before sharing a URL publicly for the first time. It takes under an hour on a typical vibe-coded app and catches most of the common issues.
sk_, pk_, secret, password,
api_key. If any of those appear as literal values in code files
(not in .env), move them to environment variables immediately..gitignore. Confirm your
.env file is listed there. Run git status and verify no
.env file appears in the list of files to be committed.*), tighten it to your specific
frontend domain before going live.The checklist above handles the most common issues. But some situations call for a real security review by a professional:
Answer before opening — recalling it is what makes it stick.
Anyone — including unauthenticated users. Supabase tables default to open when RLS is not enabled. Any request, authenticated or not, that knows the table name can read every row. Turn on RLS and define policies that restrict reads to the row's owner or to authenticated users with the right role.
'; DROP TABLE orders; --. What kind of attack is this, and what's the defence?SQL injection. The attacker is trying to sneak a destructive database command inside your query by closing the intended string early. The defence is to use parameterized queries (your database library's built-in method of passing values separately from the query text) — the value is then treated as data, never as executable SQL.
Revoke the key immediately in the OpenAI dashboard — before doing anything else. A key in a public repo is compromised the moment it lands there. Rotation is the only fix; git history rewrites don't help because bots cache commits in seconds. Then create a fresh key, store it in your environment variables, and add .env to .gitignore.
Read this next (primary source)
OWASP Top Ten — the security industry's canonical list of the most critical web application vulnerabilities, maintained by a nonprofit. You don't need to read every item in depth — scan the category names and descriptions. You'll recognise several from this lesson. It's the same list professional security engineers use as their baseline. See RESOURCES.md for more.
New terms this lesson — SQL injection, Row Level Security, CORS, parameterized query, OWASP — are in the course glossary. Keep it open.