Senft Web Studio

Blog

What vibe-coded apps get wrong about security

By Zach Senft6 min read

The pitch for AI-assisted development is real: you can go from idea to deployed app in a weekend. But the security model of most AI-generated apps is essentially "it works in the demo." That gap is where things get expensive.

I have reviewed apps built with GPT, Claude, Cursor, and other AI tools. The features work. The auth, API design, and data handling are almost always broken in the same predictable ways. Here are the five that come up every time.

1. No rate limiting on any endpoint

AI generates endpoints that respond to every request. It does not add rate limiting because you did not ask for it and because "make me a login endpoint" does not imply it. The result is an API that will respond to 100,000 requests per second just as cheerfully as it responds to 1. Credential stuffing attacks, account enumeration, and billing abuse all start here.

The fix is straightforward: a middleware layer (express-rate-limit in Node, slowapi in Python, or a Cloudflare rule) applied at the route level. The hard part is knowing you need it before a bill arrives or an account is compromised.

2. API keys in the client bundle

When you ask an AI to connect your app to a third-party service, it writes the integration code where the context is: in the component or the frontend function where you asked the question. That means the API key lives in the client bundle, which means anyone who opens DevTools has it.

This is not an edge case. It is the default output of AI-generated integrations unless you specifically instruct otherwise. Stripe keys, OpenAI keys, Supabase service-role keys, and Twilio credentials all show up regularly in client-side JavaScript in AI-generated apps.

3. Broken or missing authorization checks

Authentication (is this person who they say they are?) is usually present. Authorization (is this person allowed to do this thing?) is usually not. AI generates route handlers that check "is there a valid token" but skip "does this token belong to someone who owns this resource."

The practical result: users can access, modify, or delete other users' data by incrementing an ID in the URL or swapping a user_id in a request body. This is called an IDOR (Insecure Direct Object Reference) and it is one of the most common vulnerabilities in apps built without a deliberate authorization model.

4. Unvalidated inputs throughout the stack

AI writes code that trusts inputs. Forms accept whatever the user types. API endpoints process whatever is sent in the body. This opens injection vectors at every layer: SQL injection if you are using raw queries, NoSQL injection if you are not, XSS if you are rendering user input in HTML, and path traversal if you are doing anything with file names.

Validation libraries exist (zod, joi, yup) and AI will use them if you ask. The problem is the default output does not include them, and most founders do not know to ask.

5. Overprivileged database access

AI-generated Supabase and database configurations default to convenience: service-role keys used where anon keys should be, row-level security policies that are too broad or missing entirely, and database users with far more permissions than the operations they are performing require.

When something goes wrong in any of the layers above, overprivileged database access is what turns a limited exploit into a full data breach.

The pattern behind all five

None of these are bugs. They are the correct output for the prompts that generated them. AI tools optimize for working code given the context you provided. Security context is almost never provided, so security output is almost never generated.

The fix is not to stop using AI tools. It is to have an engineer review what they produced before it goes to production with real users and real data.

Ready to put this to work?

Get a systematic review of your app before your users find the gaps first.