What protects a vibe-coded app?
Six things, in this order. Get secrets out of the browser. Turn on Row Level Security with real policies. Add an ownership check to every endpoint that returns user data. Validate input and escape output. Rate limit anything that costs money or sends email. Then scan the deployed app before you tell anyone about it.
Everything below expands those six into a sequence you can work through in a weekend.
Why AI-generated code keeps shipping broken
AI coding tools are trained to make things work. Nobody trained them to make things safe, because “safe” has no error message. A missing permission check does not throw. The page loads. The feature works. The vulnerability just sits there.
The measured picture as of mid-2026:
| Finding | Number | Source |
|---|---|---|
| AI-generated code introducing security flaws | 45% | Veracode GenAI Code Security Report, 2025 |
| Vibe-coded production apps with security issues | 65% | Escape.tech scan of 1,400+ live apps |
| Those apps with at least one critical vulnerability | 58% | Escape.tech |
| AI-generated samples containing XSS flaws | 86% | Georgetown CSET, across five major LLMs |
| Copilot suggestions vulnerable in security-sensitive scenarios | ~30% | GitHub disclosure |
| Secret-leak rate, AI-assisted commits vs human baseline | 3.2% vs 1.5% | Cloud Security Alliance, 2026 |
| New hardcoded secrets in public GitHub commits during 2025 | 28.65 million (up 34% year on year) | GitGuardian State of Secrets Sprawl 2026 |
| CVEs attributed to AI-generated code, Jan to Mar 2026 | 6 → 15 → 35 | Georgia Tech SSLab, Vibe Security Radar |
Two of those deserve a second look.
The Georgia Tech number is a slope, not a snapshot. Their Systems Software and Security Lab traces each published CVE back to the commit that introduced it, then works out whether an AI tool wrote that commit. Six in January. Thirty-five in March. That curve is what a new category of vulnerability looks like when it starts getting reported properly.
The second is more uncomfortable. Research suggests that asking the AI to fix a security problem tends to raise the count of critical vulnerabilities by around 37.6% rather than lower it. The model patches the symptom you described and rewrites surrounding logic while it is in there. Stack Overflow’s 2025 survey found 84% of developers use or plan to use AI tools, while only 29% trust what those tools produce. People are shipping code they already do not trust.
The nine holes that show up again and again
Before the fixes, know the shapes. Almost every vibe-coded breach falls into one of these.
- Exposed secrets. API keys, database credentials and payment tokens sitting in frontend JavaScript where anyone can read them. Football Australia left AWS access keys in its website source for over 700 days.
- Missing Row Level Security. Database tables with no access policy at all. The single most common flaw in Supabase-backed apps. A CVE against Lovable covered exactly this: through 15 April 2025, weak default RLS let unauthenticated attackers read and write arbitrary tables in generated sites.
- Broken object-level authorisation, also called BOLA or IDOR. Your endpoint returns order number 1041 because the URL said 1041. It never checks whether the logged-in user owns order 1041. Change the number, read someone else’s data. This is what leaked private messages in the Tea app.
- No input validation. SQL injection, command injection, and the classic file upload that accepts a .php file into a web-accessible folder.
- Cross-site scripting. User input rendered straight into the page. An attacker stores a script in a comment field and it runs in every visitor’s browser.
- No rate limiting. Login forms brute-forced. Signup forms stuffed with bots. Your own AI API endpoint called ten thousand times overnight by someone who found it and now has a free chatbot on your credit card.
- Server-side request forgery. Any feature that fetches a URL the user supplies. In a December 2025 study by Tenzai, five out of five major AI coding agents introduced SSRF in the same feature type. Five out of five.
- Slopsquatting. Language models invent package names roughly 20% of the time. Attackers register those invented names on npm and PyPI and wait for someone to run the install command the AI suggested.
- Security misconfiguration. Public storage buckets, debug mode left on in production, verbose error pages printing stack traces, admin routes with no auth, CORS set to allow everything.
Part 1: The pre-launch sequence
Work through these in order. The first four cover most of the real risk.
Step 1: Find every secret in your code
Open your project and search the whole codebase, not just the files you remember editing, for these strings: sk-, service_role, SECRET, PASSWORD, API_KEY, TOKEN, Bearer, AKIA.
Then do the harder version. Deploy your app, open it in Chrome, press F12, go to the Sources tab and search the bundled JavaScript for the same strings. This is what an attacker does in their first ninety seconds. If you can find a key there, so can they.
The rule that catches most people: any environment variable prefixed NEXT_PUBLIC_ or VITE_ is shipped to the browser by design. Those prefixes are a public broadcast. A variable called NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY is not a configuration choice, it is a published admin password.
Step 2: Move secrets to the server and rotate the exposed ones
Every paid API — OpenAI, Anthropic, Stripe, Twilio, SendGrid, Razorpay — must be called from a server function, never from the browser. The pattern is a proxy: your frontend calls your own endpoint, your endpoint holds the key and calls the provider, the response comes back. On Netlify that is a Netlify Function. On Vercel, an API route or Edge Function. On Supabase, an Edge Function.
If a key has ever been in frontend code, in a public repo, or in a screenshot you posted while asking for help, treat it as compromised. Rotate it in the provider’s dashboard. Removing it from the code does nothing to a key someone already copied.
Step 3: Turn on Row Level Security and write policies that mean something
For every table in your public schema: RLS enabled, and at least one policy per operation that references the logged-in user.
Enabling RLS with no policies blocks everything, which breaks the app, which is why AI tools often just skip it. The other failure is worse because it looks correct:
— Broken. This is identical to having no RLS at all.
CREATE POLICY “users can read” ON profiles
FOR SELECT USING (true);
— Correct. Scoped to the authenticated user.
CREATE POLICY “users read own profile” ON profiles
FOR SELECT USING (auth.uid() = user_id);
Write policies for SELECT, INSERT, UPDATE and DELETE separately. A table that is read-protected but write-open is still a breach waiting to happen. Then test them: log in as user A, try to fetch user B’s row, confirm you get nothing back.
Step 4: Add an ownership check to every endpoint
This is the fix for BOLA, and it is the one AI tools miss most reliably because the code works perfectly without it.
Go through every route that accepts an ID — /api/orders/:id, /api/documents/:id, /api/users/:id — and confirm two things happen before any data is returned. Is there a valid session? Does the record belong to that session’s user? Both, on every route, including the ones you added last week.
Then test it. Create two accounts. Log in as the first, note an ID from the URL. Log in as the second, put the first account’s ID in the URL. If you can see the data, you have the same bug the Tea app had.
Step 5: Validate input, escape output
Validate on the server. Client-side validation is a user experience feature, not a security control, and anyone can bypass it with a direct API call.
Use a schema validator such as Zod or Yup on every endpoint that accepts data. Set expected types, lengths and formats. Reject anything that does not match rather than trying to clean it.
For output, use your framework’s default escaping and stop overriding it. In React that means avoiding dangerouslySetInnerHTML. If you genuinely need to render user HTML, run it through DOMPurify first.
Use parameterised queries for every database call. If you can see string concatenation anywhere near a SQL statement, rewrite it.
Step 6: Rate limit anything that costs money or sends messages
Login, signup, password reset, contact forms, search, and every endpoint that calls a paid API. Without limits, one script running overnight can drain an API budget or fill your database with junk accounts.
Sensible starting points: five login attempts per IP per minute, three password resets per email per hour, ten AI calls per authenticated user per minute, one hundred general API requests per IP per minute. Tune from there.
Cloudflare gives you a usable layer of this at the edge for free. Upstash Ratelimit works well inside serverless functions. Whichever you pick, apply it in the function itself, not only in the frontend.
Step 7: Lock down uploads and storage
Storage buckets default to private unless the files are genuinely meant for a public CDN. Private buckets get policies scoped by user, the same way tables do.
For uploads: restrict by MIME type on the server, cap the file size, generate a new random filename rather than trusting the one the user sent, and store files outside the web root or behind signed URLs. Never execute anything from an upload directory.
Step 8: Fix authentication and sessions
Use a managed auth provider. Supabase Auth, Clerk, Auth0, NextAuth. Do not let AI write you a custom login system, and do not store passwords yourself. If you have inherited a custom system, check that passwords are hashed with bcrypt or Argon2 and never encrypted or stored in plain text.
Beyond that: sessions expire, tokens are invalidated on logout, password reset links are single-use and short-lived, and admin accounts have two-factor authentication switched on. Check that your password reset flow does not reveal whether an email address exists in your database.
Step 9: Set security headers and fix CORS
Access-Control-Allow-Origin: * on an authenticated API means any website can make requests on your users’ behalf. Set it to your actual domain.
Add these headers, which most hosts let you configure in a single file:
Content-Security-Policy: default-src ‘self’
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), microphone=(), camera=()
Then check the result at securityheaders.com. It takes thirty seconds and gives you a grade.
Step 10: Check what the AI told you to install
Before running any install command an AI gave you, look the package up on npm or PyPI. Check the download count, the publish date and the repository link. A package with 40 downloads published three weeks ago, with a name suspiciously close to a popular library, is the slopsquatting attack working exactly as designed.
Run npm audit or pip-audit and fix what it flags. Turn on Dependabot in your GitHub repository so you get told when something you depend on gets a published vulnerability.
Step 11: Turn on logging and alerts
You cannot respond to a breach you never notice. At minimum, log failed login attempts, permission-denied errors, and unusual request volume from a single IP. Set a billing alert on every paid API, because a sudden spend spike is often the first visible sign of an abused endpoint.
Sentry handles error monitoring on a free tier. Your host almost certainly has request logs you have never opened.
Step 12: Scan before you launch
Run the deployed application, not the code, through a scanner. Several now target this specific problem: Escape.tech, Snyk, Semgrep, and platform-native options such as Replit’s Security Agent, which shipped in April 2026 and runs a full vulnerability scan in under an hour.
Fix everything marked critical or high before launch. Log the medium findings and schedule them.
Part 2: What breaks on each platform
The failure modes are consistent enough to predict from your stack alone.
| Platform | What usually goes wrong | First thing to check |
|---|---|---|
| Lovable | RLS disabled on Supabase tables, exposed anon keys, BOLA on generated routes | Every table has RLS on with a policy referencing auth.uid() |
| Bolt.new | Hardcoded API keys in frontend, no auth on Netlify Functions, weak CORS, unthrottled endpoints | Every paid API call goes through a Netlify Function proxy |
| Replit | Public-by-default repls, hardcoded credentials, missing auth on Agent-generated endpoints, no rate limiting | Repl visibility set to private, secrets in the Secrets pane |
| v0 and other UI-first tools | Security gap follows whatever backend you wired up yourself | The backend, since the generator did not make one |
| Cursor and Claude Code | Inherits whatever the surrounding codebase does, plus whatever you accepted without reading | Diff review on every accepted change touching auth or data access |
One trap specific to Replit: forking a repl does not carry security configuration with it. A fork of a secure project is not automatically a secure project.
Part 3: Prompt the AI differently
You are not going to stop using these tools, and you should not. But how you ask changes what you get.
Ask for the risks before you ask for the code. Try: “Before you write this, list the security risks in this approach and how you will handle each one.” Chain-of-thought security prompting measurably reduces insecure output. The model has the knowledge. It just does not volunteer it.
Name the threat model in the prompt. “Write an endpoint that returns a user’s orders, where a logged-in user must only be able to see their own orders, and any attempt to request another user’s ID returns 403” produces different code from “make an orders endpoint.”
Ask for the tests too. “Write a test that proves user A cannot read user B’s data” gives you the ownership check for free, because the model has to build something that passes.
Never accept “use the service role key” as a fix. When a query fails because of RLS, AI tools frequently suggest swapping in the service role key. That key bypasses RLS entirely. Accepting that suggestion converts a blocked query into a full database compromise. The correct fix is to write the policy properly or move the operation into an Edge Function.
Review the diff on anything touching authentication, permissions, database access, file handling or payments. You do not need to read every line the AI writes. You need to read those lines.
Part 4: Learn to think like an attacker
You will find your own bugs faster once you understand how people look for them. The best free resource is pwn.college, run out of Arizona State University. Its dojos teach exploitation from first principles through hands-on challenges rather than reading. You do not need the binary exploitation modules; the web security and access control material alone will change how you look at your own endpoints.
Alongside that, PortSwigger’s Web Security Academy is free and covers the exact vulnerability classes in this guide with practical labs. OWASP publishes the Top 10, which is the reference list professional testers work from, and the API Security Top 10, which is closer to what most vibe-coded apps actually expose.
An afternoon on either site will teach you more about your own app than another week of building features.
Part 5: The thirty-minute emergency audit
If you already have users and you have done none of the above, do this now, in this order.
Minutes 1 to 5. Open your live site, press F12, search the Sources tab for service_role, sk-, and SECRET. Anything you find gets rotated today.
Minutes 5 to 10. Open your database dashboard. Confirm RLS is enabled on every table in the public schema and that each has at least one policy. Any table without one is public.
Minutes 10 to 20. Create two test accounts. Try to read account A’s data while logged in as account B by changing IDs in URLs and API calls. Try three or four endpoints.
Minutes 20 to 25. Check your storage buckets are private. Check your CORS setting is your domain and not *. Run your URL through securityheaders.com.
Minutes 25 to 30. Set a billing alert on every paid API. Turn on two-factor authentication for your hosting, database and domain registrar accounts.
That is not a full security review. It closes the doors that get walked through most often.
If you think you have already been breached
Move in this order. Rotate every credential first, starting with database and payment keys, because containment matters more than diagnosis. Preserve your logs before anything overwrites them. Work out what data was reachable and for how long. Fix the specific hole. Then handle the disclosure obligations, which under India’s DPDP Act and the GDPR are measured in hours, not weeks. Tell affected users before they find out from somewhere else.
Do not delete evidence while trying to tidy up. Do not quietly patch and say nothing if personal data was exposed.
The uncomfortable summary
Vibe coding did not create these vulnerabilities. Broken access control and hardcoded secrets have topped the OWASP list for years. What changed is who is shipping code, and how fast, and how few of them have ever been asked to think about an attacker.
The gap is not knowledge, it is sequence. Nobody tells you to check RLS before launch, so nobody does, until Wiz publishes a report with your product’s name in it.
Work through Part 1. It is a weekend, once. The alternative is finding out how your app was built at the same time as everyone else.
Creative Nexus builds and secures web applications for brands across India and the US. If you have shipped something built with AI tools and want a second pair of eyes on it before your users find the problems, get in touch.
Frequently Asked Questions
Is vibe coding safe for production apps?
The tools are fine. Shipping their output unreviewed is not. Roughly 45% of AI-generated code contains a security flaw, and in professional audits of vibe-coded apps only about one in eleven comes back clean. Vibe coding is safe for production when a human reviews everything touching authentication, data access and payments before launch.
Can someone actually hack an app built with Lovable or Bolt?
Yes, and it usually takes no skill. Most incidents are not exploits, they are open doors: a database with no access policy, an API key visible in browser dev tools, an endpoint that returns any record you ask for. The Moltbook breach exposed 1.5 million tokens through a database that had been left publicly readable and writable.
What is the single most common security hole in AI-generated apps?
Missing or broken Row Level Security on the database. AI tools rarely enable it by default, and when it does get enabled without policies the app breaks, so the tool often removes it again to keep things working.
My Supabase anon key is visible in the browser. Have I been hacked?
Not necessarily. The anon key is designed to be public and is safe to expose, but only if RLS policies are doing the access control behind it. If your tables have no policies, that anon key gives anyone with your project URL full read access to your data. Check the policies, not the key.
What is the difference between the anon key and the service role key?
The anon key is scoped by your RLS policies and belongs in the browser. The service role key bypasses RLS completely and grants full administrative database access. It belongs only in server-side environments. If the service role key has ever been in frontend code or a public repository, rotate it immediately.
Do I need Row Level Security if I already have login?
Yes. Login controls who gets into your application. RLS controls what the database will hand out to a request. Because Supabase exposes a REST endpoint directly, an attacker with your project URL and anon key can query tables without ever visiting your frontend. Your login screen is not in that path.
I found an API key in my GitHub repo. What do I do right now?
Rotate the key first, in the provider's dashboard. Then remove it from the code and add the file to .gitignore. Then check the provider's usage logs for activity you do not recognise. Rotation comes first because everything else is meaningless while the old key still works.
Will deleting the commit remove the leaked key from GitHub?
No. Git history persists, forks keep their own copies, and public repositories are scraped by automated bots continuously, often within minutes of a push. Treat any key that has been pushed publicly as permanently compromised. Rewriting history is optional cleanup; rotation is mandatory.
How do I stop people running up my OpenAI or Claude API bill?
Three layers. Never call the provider from the browser, always proxy through your own server function. Require authentication on that function. Rate limit per user, not per IP, since IPs are cheap to change. Then set a hard spending cap in the provider dashboard as a backstop.
Can I just ask the AI to make my app secure?
Not as a single instruction. A general request produces general changes. Worse, iterating with AI on security has been found to raise critical vulnerability counts by around 37.6%, because the model rewrites working logic while patching the thing you named. Ask about one specific vulnerability class at a time and review each diff.
How much does a security review of a vibe-coded app cost?
Automated scanning runs from free to a few thousand rupees a month. A manual review by someone who knows this category typically runs from around ₹40,000 for a small application to several lakh for something handling payments or sensitive personal data. Compare that against breach notification costs and the DPDP Act's penalty ceiling of ₹250 crore.
Do I need a penetration test before launch?
For a side project with no personal data, no. For anything handling payments, health information, identity documents or children's data, yes, and increasingly your enterprise customers will ask for the report before signing. In between, automated scanning plus a manual review of authentication and data access covers most of the risk.
What is a BOLA vulnerability and why does AI keep creating them?
Broken object-level authorisation, also called IDOR. Your endpoint returns record 1041 because the request asked for 1041, without checking whether the requester owns it. AI tools create these constantly because the code works perfectly in testing. You only notice when someone else's data appears.
Is it safe to store passwords in my own database?
Only if they are hashed with bcrypt or Argon2, never encrypted and never in plain text. The better answer is not to store them at all. Use Supabase Auth, Clerk, Auth0 or NextAuth and let a specialist handle password storage, reset flows, session management and two-factor authentication.
How do I add rate limiting without a backend developer?
Cloudflare's rate limiting rules work at the DNS level and need no code changes. For serverless functions, Upstash Ratelimit is a few lines. Most managed auth providers include login throttling already. Start with Cloudflare, then add per-user limits inside the functions that cost you money.
My app has no login. Do I still need security?
Yes. Rate limiting stops your contact form becoming a spam relay. Input validation stops injection. Security headers stop clickjacking and content injection. And check what your app writes as well as what it reads, since a public form writing to an unprotected table is still a public database.
Is Supabase itself insecure?
No. Supabase runs on PostgreSQL and its security primitives are sound. Almost every Supabase-related breach traces back to configuration: RLS never enabled, a policy written as USING (true), or the service role key shipped to the browser. The platform is safe. The default state of a table generated by an AI often is not.
What is slopsquatting and how do I avoid it?
Language models invent package names that do not exist roughly 20% of the time. Attackers register those invented names on npm and PyPI with malicious code inside. Avoid it by checking every package before installing: download count, publish date, linked repository, and whether the name matches what the official documentation says.
How do I test my own app without being a hacker?
Yes, unless you have a specific reason not to. Replit repls are public by default on some plans, which means your code and sometimes your configuration are readable by anyone. Private by default, and keep secrets in the Secrets pane rather than in files regardless.
Do I need to worry about prompt injection if my app uses AI features?
Yes, unless you have a specific reason not to. Replit repls are public by default on some plans, which means your code and sometimes your configuration are readable by anyone. Private by default, and keep secrets in the Secrets pane rather than in files regardless.Content
Do I need to worry about prompt injection if my app uses AI features?
Yes, if user input reaches a model that can act on the result. Treat model output as untrusted input, never let it trigger database writes or API calls without validation, keep system instructions server-side, and cap what the model is permitted to do rather than relying on instructions telling it what not to do.
What security do I need for DPDP or GDPR compliance in India?
Both require security measures appropriate to the risk, breach notification within tight windows, and a lawful basis for processing. Practically: encryption in transit and at rest, access controls that actually restrict access, a data deletion route, a privacy policy that matches what you do, and a record of what you collect and why. The DPDP Act's penalties reach ₹250 crore for failing to protect personal data.
How often should I re-audit an AI-built app?
Run automated scans on every deploy. Do a manual review of authentication and data access every quarter, and after any significant feature addition. AI-assisted development compounds: each new feature inherits the security assumptions of the last, so a codebase that was clean six months ago may not be now.







