Vibe-Coded Apps Are a Security Nightmare: A Builder's Checklist
A tester broke a vibe-coded chat app in 30 minutes. Here are the flaws to avoid and the pre-ship checklist every builder needs.
A tester broke a vibe-coded chat app in 30 minutes. Here are the flaws to avoid and the pre-ship checklist every builder needs.

A security-minded developer recently poked at a chat app that had been shared in a vibe-coding community. Within thirty minutes, they had completely broken it. The UI was clean, the feature set was surprisingly deep, and from the outside it looked like a finished product. Under the hood it was a disaster: an OpenAI key sitting in the public JavaScript bundle, a wide-open Firebase database, identity managed in localStorage, and admin checks that lived entirely in the browser. This is not a rare horror story — it is the default failure mode of apps built fast with AI. If you are shipping vibe-coded software that touches users, money, or private data, this is the pre-ship checklist you cannot skip.
The pattern is remarkably consistent, and it is not because AI writes insecure code out of malice or incompetence. It is because security is invisible in a demo. When you prompt an agent to "build a chat app with rooms and an admin panel," it produces something that works — and working is all you can see. The exposed key still lets the app call the model. The open database still returns data. The client-side admin check still hides the button. Everything looks correct right up until someone opens the network tab or queries your backend directly.
Because the happy path works, the builder ships. And because the AI optimized for a functioning feature rather than a threat model, the app trusts the browser for things the browser must never be trusted with: identity, roles, permissions, and moderation. The result is an app that is genuinely useful and genuinely wide open at the same time.
In the broken chat app, nearly everything that could be wrong was wrong, and the specific issues are the ones you will find over and over in AI-built projects:
With the database rules wide open, the tester could adjust their own user rank, falsify other users’ messages, change usernames, and derank anyone. Private password-protected rooms were readable by everyone. The "ban" was a suggestion. None of this required sophisticated exploitation — just looking at what the app actually sent and stored.
Almost every item on that list reduces to one mistake: trusting browser and device state. The frontend is fully controllable by whoever is using it. Anything that lives only in JavaScript — a role flag, a balance, a message count, an "isAdmin" boolean, a hidden admin route — is not a security control. It is a decoration. Real authorization has to be enforced on the backend, in database rules or server-side checks that the user cannot touch.
This is the mental model AI agents do not have unless you give it to them. They will happily store a user’s subscription status in a writable record, gate an admin panel with a client-side conditional, and call it done. The security boundary has to be something you specify and verify yourself, because a passing demo will never reveal its absence.
Managed backends like Firebase and Supabase are a gift to solo builders — they hand you auth, a database, and realtime out of the box. But they ship with a security model that assumes you will configure it, and AI-generated setups almost never do. In the broken chat app, the Firebase rules were wide open, which meant every private room, admin log, report, and ban list was readable and writable by anyone who found the config. And the config is always findable: it lives in the frontend by design.
The fix is not to hide the config — you cannot, and you are not meant to. The fix is row-level security and database rules that enforce who can read and write what. On Supabase, that means turning on row-level security and writing policies for every table; on Firebase, it means locking down the default-open rules to check auth and ownership on each path. An agent will happily leave both in test mode, where anyone can do anything, because test mode is what makes the demo work. Treat "are my database rules actually restrictive?" as a mandatory checklist item, not a nice-to-have.
It is tempting to think of these flaws as theoretical, the kind of thing that only matters once you have thousands of users. The opposite is true. An exposed OpenAI or Anthropic key can be scraped by an automated bot within hours and used to run up thousands of dollars in API charges on your account before you notice. An open database is not just a privacy issue — it is a data-integrity issue, because attackers can write as well as read: falsifying messages, granting themselves credits, or corrupting your records.
And the reputational damage compounds the financial one. If your app manages private conversations, payments, or any personal data, a breach is not a bug report — it is a breach-notification obligation and a permanent dent in user trust. The asymmetry is stark: the flaws take minutes to prevent and can take a business to close it down when exploited. For a solo builder or small team, that is not a risk worth carrying to save ten minutes of testing.
Before any AI-assisted app goes live — especially anything with users, money, moderation, private rooms, or admin panels — run through this list. It maps directly to the wounds above and takes minutes, not hours:
The single highest-value action is testing your backend while unauthenticated. Most of these apps look secure precisely because the developer only ever tests as a logged-in, well-behaved user through the UI. An attacker does neither. Open the network tab, hit your API without a session, and see what comes back.
You do not have to become a security engineer to close most of this gap — you have to make security part of the spec instead of an afterthought. When you ask an agent to build a feature, add the threat model to the prompt: "enforce all authorization in Firebase security rules, not client-side; never expose the API key in the frontend — proxy model calls through a backend function; store roles and balances in records the user cannot write." The model is fully capable of building this correctly. It just will not do so unless you ask.
It also helps to run a dedicated review pass. After the app works, start a fresh session and prompt: "act as a penetration tester — find exposed secrets, unprotected database paths, and client-side trust issues in this codebase." Agents are often better at finding these flaws than at avoiding them in the first place, so use that. Tools and skills exist specifically for this kind of adversarial self-review, and they cost far less than a breach.
There is a reframe worth internalizing here: security is not a separate discipline bolted on at the end — it is just another set of requirements you can hand to your agent, as long as you know to state them. The same tool that built your open Firebase rules can write locked-down ones. The same agent that dropped your key in the bundle can scaffold a backend proxy that keeps it server-side. The gap is never capability; it is specification. You get insecure output because you asked for a feature and forgot to ask for it to be safe.
So bake it into how you work. Keep a short reusable prompt block — enforce authorization server-side, proxy all model calls, never trust the client for identity or roles — and paste it into every build session. Then run one adversarial review pass before shipping. Two small habits, added to a workflow you already have, close the overwhelming majority of the holes that make AI-built apps a liability. The builders who win with these tools will be the ones who move fast and remember that the demo is not the finish line.
The optimistic reading of all this is that none of it is hard. AI tools genuinely do let you build fast, and that speed is real and worth celebrating. The problem is not that vibe coding is unsafe — it is that "it works in the demo" and "it is safe to expose to the public" are two completely different bars, and the demo tells you nothing about the second one. Every issue in the broken chat app was preventable with a few minutes of checking.
So keep building fast. Then, before you ship anything that touches users, money, moderation, or private data, spend the ten minutes it takes to test your backend as a hostile stranger. Grep for keys, query your database logged out, and assume the client is the enemy. That small deliberate pause is the entire difference between a product people can trust and a headline waiting to happen. Speed and security are not a trade-off here — you just have to remember to do the second one.
Get the weekly digest for AI builders & vibe coders. Curated tools, resources, and stories. Skip the scroll.