← Back to Blog

Your Vibe-Coded App Is a Security Disaster Waiting to Happen

By · · 13 min read

#webdev#security#javascript#devops

AI coding tools let you ship full-stack apps overnight — but speed kills security. From JWTs in localStorage to wildcard CORS and exposed databases, vibe-coded apps are riddled with exploits. Here's a 31-point security checklist to audit and harden everything before it costs you.

Your Vibe-Coded App Is a Security Disaster Waiting to Happen
Your Vibe-Coded App Is a Security Disaster Waiting to Happen And here's the 31-point checklist that can save it. --- There's a new kind of developer energy in the air. You have an idea at 11 PM. You open a chat with an AI coding assistant, describe your vision, and by 2 AM you have a working full-stack app — auth, database, REST API, frontend. You ship it. You share it. People use it. That's vibe coding. It's fast, it's exciting, and it's genuinely changing how software gets built. There's just one problem: speed and security almost never coexist by default. When you're vibing through an app at 2 AM, you're not thinking about HttpOnly cookies, Row-Level Security, or CORS wildcard vulnerabilities. You're thinking about shipping. And the AI helping you? It's optimized to make working code, not hardened code. It'll give you JWTs stored in localStorage, in your CORS config, and database calls that concatenate raw user input — all of it syntactically correct, all of it dangerously broken in production. This post is a wake-up call. We're going to break down the most critical security blind spots in vibe-coded apps and map them to a concrete, 31-point security checklist you can use to audit and harden any project you've built. --- The Anatomy of a Vibe-Coded Vulnerability Before we dive into the checklist, let's set the scene. Imagine you've built a SaaS app. Users can sign up, create projects, and manage their data. It works. Your friends think it's cool. You've got 50 sign-ups. Here's what your stack probably looks like under the hood, if you weren't security-conscious: - JWT stored in (hello, XSS attack surface) - protected only by a frontend conditional render - CORS set to because it was the only way to make the fetch work - Database password: or literally - User-facing error messages that say things like - No rate limiting on your endpoint Each of these is a real, exploitable vulnerability. Not theoretical. Let's fix them. --- Part 1 — Authentication & Authorization: The Foundation You're Getting Wrong The Frontend Is Not a Security Boundary This is the #1 mistake in vibe-coded apps. A typical AI-generated auth flow might hide the admin dashboard behind a check. That conditional does absolutely nothing to prevent a user from directly calling . Every private route must be authenticated at the server/API level. The frontend is just a UI — anyone can talk to your API directly using curl or Postman. IDOR: The Vulnerability Nobody Talks About Insecure Direct Object Reference (IDOR) is terrifyingly common. Here's how it happens: A user is viewing their own profile at . They change the URL to . Your server fetches and returns someone else's data because you never checked if . Always enforce ownership checks. If a user is requesting a resource, verify they own it before returning it. This seems obvious, but vibe-coded apps almost universally skip it. Session Termination Is Not Just Clearing Cookies When a user logs out, you must invalidate the token on the server, not just delete it client-side. A client-side-only logout means anyone who captured that JWT (from logs, memory, XSS) can still use it until it naturally expires. Maintain a server-side blocklist for revoked tokens, or use short-lived tokens with a refresh rotation strategy. --- Part 2 — Token Management: The JWT Trap Stop Putting JWTs in localStorage This is the most common, most cited, and most ignored security mistake in modern web apps. is accessible to any JavaScript running on your page. If your app has an XSS vulnerability (and most do — even a single compromised third-party script can cause one), an attacker can steal every token stored there with a one-liner: Store JWTs in , cookies. These cookies are invisible to JavaScript entirely. The browser sends them automatically on requests but no script can read them. Secrets in Your Code Are Time Bombs How many public GitHub repositories have committed in plain text? Thousands. Tens of thousands. Your JWT secret needs to be: - Long (256-bit minimum) - Cryptographically random - Injected via environment variables - Never, ever in your codebase And yes, must be in . Always. No exceptions. Token Expiry Is Non-Negotiable Tokens that never expire are a permanent key to your kingdom. If one leaks, the attacker has access forever. Set access tokens to expire in 15 minutes. Use a refresh token rotation mechanism — when a refresh token is used, it is invalidated and replaced with a new one. If an old refresh token is detected being used (replay attack), revoke all sessions for that user immediately. --- Part 3 — API Security: The Open Door CORS Wildcards Are Not "Just for Development" Every vibe-coded app I've seen starts with: "Later" never comes. The wildcard stays in production. A wildcard CORS policy means any website on the internet can make authenticated requests to your API from a user's browser. Combined with cookie-based auth, this enables Cross-Site Request Forgery (CSRF) attacks. Restrict CORS to your exact frontend domain: Brute Force Is Embarrassingly Easy to Prevent Your endpoint with no rate limiting can be hammered at thousands of requests per second by a bot. This isn't an advanced attack — it's a script that runs in minutes. Implement rate limiting on all auth endpoints: AI API Integrations Are a Billing Exploit Waiting to Happen If your app calls OpenAI, Anthropic, or any LLM API on behalf of users — and you have no quotas — a single malicious user can trigger thousands of API calls and bill you into bankruptcy overnight. Implement hard per-user quotas, daily limits, and request timeouts on every AI integration route. This isn't optional — it's existential for indie products. --- Part 4 — Secrets Management: Simple Rules, Infinite Impact The rules here are short, because they're absolute: 1. No private API keys in frontend code. Ever. All third-party calls go through your backend. 2. All secrets in . Every secret. No exceptions. 3. in . Check this right now if you haven't already. 4. No hardcoded credentials anywhere — not in code, not in comments, not in commit messages, not in documentation, not in AI prompts you paste into chat interfaces. One leaked AWS key can cost you tens of thousands of dollars. One leaked Stripe secret key can allow fraudulent charges. These are not hypotheticals — they happen every week. --- Part 5 — Database Security: The Layer Most Devs Never Think About Your Database Should Not Be on the Public Internet If you can run from your laptop without a VPN or IP whitelist — your database is exposed. Attackers run automated scanners looking for open database ports 24/7. Restrict database access to your VPC (if on AWS/GCP) or whitelist specific backend IPs only. Root Credentials Are for Setup, Not Application Use Your app should connect to the database with a dedicated user with restricted permissions — only the tables and operations it actually needs. If your app only reads and writes user data, it doesn't need privileges. This limits the blast radius if your application-level code is ever compromised. Row-Level Security: The ORM Isn't Enough If you're using PostgreSQL (or Supabase), Row-Level Security (RLS) is a database-level guarantee that users can only see their own rows — even if your application code has a bug that would otherwise expose someone else's data. This is a second layer of defence that many vibe-coded apps completely miss. Parameterized Queries Are Not Optional SQL injection has been the #1 web vulnerability for over two decades. It's still everywhere in vibe-coded apps because AI will generate string-concatenated queries if you're not careful: SELECT * FROM users WHERE email = '${email}' A well-configured ORM (Prisma, Drizzle, Sequelize) will do this for you automatically — but you need to use it properly and never drop to raw query strings. --- Part 6 — Input Validation: Don't Trust Anything from the Client Server-Side Validation Is Your Contract with Reality Client-side validation is UX. Server-side validation is security. Never conflate the two. Every request body, query parameter, and URL segment must be validated and sanitized on the server using a schema validation library like Zod (TypeScript) or Joi (JavaScript): File Uploads Are a Full Attack Surface Don't check just the file extension — a file named is trivially created. Validate the actual MIME type by reading the file's magic bytes, enforce hard size limits, and never serve uploaded files directly from your application server. Store user files in private cloud storage (S3, GCS) and serve them via short-lived pre-signed URLs that expire after a few minutes. A public storage URL that never expires is a permanent data leak. --- Part 7 — Infrastructure & Operations: The Invisible Layer HTTPS Everywhere, Always There is no excuse for unencrypted HTTP in 2025. Every route, every API call, every WebSocket connection must use TLS. Use Cloudflare, Nginx, or your cloud provider's load balancer to enforce HTTPS redirects and reject all HTTP traffic. Error Messages Are Intelligence for Attackers When your app throws an unhandled exception and returns this to the client: You've just told an attacker your internal network topology, your database technology, and your IP addressing scheme. That's free reconnaissance. Return generic error messages to clients. Log detailed errors server-side only. You Need Logs You Can Trust When something goes wrong — a breach, a billing anomaly, an unexpected data deletion — your audit logs are the difference between understanding what happened and flying blind. Log every authentication event (login, logout, failed attempts), every destructive action (deletions, role changes), and every payment transaction. These logs must be immutable — stored somewhere your application can write to but not delete from. --- The Full 31-Point Security Checklist Here's the complete checklist, organized by category. Treat every item as a gate — your app doesn't go to production until all boxes are checked. Authentication & Authorization - [ ] All private routes authenticated at server/API level - [ ] Server-side RBAC on all admin and elevated routes - [ ] IDOR prevention — ownership checks on all data requests - [ ] Logout invalidates session/token on the server Token Management & Cryptography - [ ] JWTs stored in HttpOnly, SameSite=Strict cookies (never localStorage) - [ ] JWT secrets are cryptographically strong and environment-variable-injected - [ ] Short token expiry with secure refresh token rotation API Security & Abuse Prevention - [ ] CORS restricted to exact known frontend domains (no wildcards) - [ ] Rate limiting on all auth endpoints - [ ] Throttling on compute-heavy API routes - [ ] Hard quotas and timeouts on AI/third-party API integrations Secrets Management - [ ] No private API keys or credentials in frontend code - [ ] All secrets managed via files; in - [ ] No hardcoded secrets anywhere in the codebase or docs Database Security - [ ] Database not exposed to the public internet - [ ] Strong, randomly generated database passwords - [ ] Dedicated database user with least-privilege permissions - [ ] Row-Level Security (RLS) or equivalent tenant isolation - [ ] All DB interactions via parameterized queries or ORM Input Validation & Data Integrity - [ ] Strict server-side schema validation on all inputs (Zod/Joi) - [ ] File uploads validated by MIME type with hard size limits - [ ] User files in private cloud storage; served via short-lived pre-signed URLs Infrastructure, Edge, & Network Security - [ ] HTTPS enforced everywhere; no unencrypted traffic - [ ] WAF or edge protection configured (Cloudflare, AWS WAF, etc.) - [ ] Admin panels and debug endpoints secured or disabled in production Operations, Logging, & Incident Response - [ ] Generic error messages to clients; detailed logs server-side only - [ ] Immutable audit logging for auth events, deletions, and transactions - [ ] Automated dependency scanning in CI/CD pipeline (npm audit, Dependabot) - [ ] Documented database backup schedule with tested restoration commands - [ ] Documented deployment rollback plan --- Final Thoughts: Vibe Responsibly Vibe coding is a superpower. The ability to ship products faster than ever before is genuinely transformative, especially for indie builders and solo founders. I'm not here to tell you to stop using AI to build. I'm here to tell you that shipping fast and shipping secure are not mutually exclusive — but only if you build security into your process from the start, not as an afterthought. The 31 requirements in this checklist aren't bureaucratic overhead. Each one exists because someone shipped without it, got burned, and the lesson got passed down. They represent the minimum surface area of defence for any internet-facing application. Before your next launch, run your codebase against this checklist. You'll probably find at least five things to fix. Fix them. Your users' trust — and your own reputation — depends on it. --- Built with obsession by someone who's shipped the insecure version and learned the hard way. If this helped you, share it with a fellow builder who's mid-vibe-session right now.