Vibe Coding Security: What I Learned Shipping AI-Generated Code

Three weeks after launching the KINS Sales Agent, I was scrolling through my codebase in Cursor — not looking for anything specific, just getting familiar with what Claude had built — when I saw my Stripe secret key hardcoded on line 47 of the payment handler. Not the publishable key. The secret key. The one that lets anyone charge any amount to any customer. Sitting right there in the source code, committed to my repository, deployed to production.

My vision narrowed. The specific panic of realizing something has been exposed and you do not know for how long. I checked the git history. The key had been there for nineteen days. Nineteen days where anyone who found my repository could have drained my Stripe account. Nobody did — I got lucky. But luck is not a security strategy, and the nausea I felt staring at that line of code changed how I build everything.

This is not a security tutorial. I am not a security expert. I am a solo founder who ships AI-generated code to production, and I am going to tell you what I have learned about doing that without getting burned — from a Stanford study that should terrify every vibe coder, to the specific practices that let me sleep at night.

The Number That Should Scare You

A 2024 Stanford study found that developers using AI assistants produced code with security vulnerabilities roughly 45% of the time. Almost half. And here is the part that makes it worse: those developers rated their code as more secure than developers who wrote code without AI assistance. The AI made them simultaneously more vulnerable and more confident.

I read that study six months into my vibe coding journey and it landed like a gut punch. Because I had been doing exactly that — shipping code from Claude with the breezy confidence of someone who trusted the output without understanding it deeply enough to catch the problems.

Claude is excellent at writing functional code. It handles logic, architecture, and integration with a fluency that still surprises me. But security is adversarial. It is not about making things work — it is about anticipating how someone might make things break. And AI assistants, trained on vast codebases that include both secure and insecure patterns, will sometimes produce code that works perfectly and is perfectly exploitable.

This does not mean you should not vibe code. It means you should vibe code with your eyes open.

The Mistakes I Almost Made (and the One I Did Make)

The Stripe key was the worst, but it was not the only one.

In the first version of Soulin Social, Claude generated a database query that concatenated user input directly into the SQL string. If you know what that means — SQL injection — your stomach just dropped. If you do not, here is the short version: it meant anyone could type a specially crafted string into the search bar and read, modify, or delete my entire database. Every user's content. Every saved voice profile. Every payment record.

I did not catch it. Supabase's row-level security caught it — the query was blocked by the RLS policies I had set up, almost by accident, because a tutorial I followed early on said "always enable RLS." I did not fully understand why at the time. Now I do.

Another time, Claude built a file upload feature for user avatars without any validation on file type or size. Someone could have uploaded a 500MB file and crashed my server. Or uploaded a script disguised as an image. The feature worked — it accepted files and displayed them. It just accepted everything, with zero discrimination.

These are not exotic attacks. They are the most common vulnerabilities in web applications, listed in every OWASP report for the last decade. And AI-generated code produces them because the AI optimizes for function, not defense.

What I Actually Do Now

I am not going to pretend I have become a security expert. I have not. But I have built a set of practices that are the equivalent of locking your doors and not leaving your keys in the ignition — basic, but effective against the most common threats.

Environment Variables for Everything Sensitive

After the Stripe key incident, I moved every API key, database credential, and secret token into environment variables. Claude knows how to do this — you just have to ask. My prompt now always includes: "Store all sensitive values in environment variables. Never hardcode API keys, tokens, or credentials."

I also added a .env file to my .gitignore on every project, and I check — manually, every time — that no secrets appear in the code before I deploy. It takes thirty seconds. That thirty seconds prevents the kind of mistake that takes weeks to recover from.

Supabase Row-Level Security

RLS is the single most important security feature in my stack. It works like this: instead of trusting your application code to only show users their own data, the database itself enforces the rule. Even if my code has a bug, even if someone finds a way to call my API directly, they can only see their own data because the database will not return anything else.

I enable RLS on every table, no exceptions. Claude helps me write the policies — "users can only read rows where user_id matches their auth ID" — and I test them by trying to access data I should not be able to see. Every new table gets an RLS policy before it gets any data.

Input Validation

Everything a user can type, upload, or send gets validated. Text inputs have length limits. File uploads are restricted to specific types and sizes. Email fields check for valid email format. Number fields reject letters.

This sounds obvious. It is not obvious when you are moving fast and Claude generates a form that accepts whatever you throw at it. The AI builds what works. You add what protects.

Rate Limiting

After I launched the SEO agent's public-facing API, someone ran a script that hit my endpoint 4,000 times in an hour. It did not break anything — but my Claude API bill for that day was $23 instead of the usual $0.80. Rate limiting caps the number of requests from a single IP address. I set it to 60 requests per minute for most endpoints, 10 per minute for anything that triggers an AI call.

Get essays like this — plus behind-the-scenes builds — in your inbox every week. Subscribe free →

Claude built the rate limiter in about five minutes. It should have been there from day one.

The Pre-Deploy Checklist

I keep a checklist in my Notes app. Before every deployment, I read it. It is seven items:

  1. No hardcoded secrets in the code
  2. RLS enabled on all new tables
  3. Input validation on all new user-facing fields
  4. Rate limiting on all new API endpoints
  5. Error messages do not expose internal details
  6. Authentication required on all protected routes
  7. File uploads restricted by type and size

Seven items. Takes five minutes. Catches the vast majority of the vulnerabilities that AI-generated code introduces.

What I Do Not Know

I want to be honest about the limits of my security knowledge.

I do not know how to do a proper penetration test. I do not fully understand cross-site scripting beyond the basics. I cannot evaluate the cryptographic decisions Claude makes when implementing authentication. I have never conducted a security audit.

For production software handling payments and personal data, this is a real gap. I compensate in three ways: I lean heavily on Supabase's built-in security features (which are maintained by a team of actual security engineers), I use Stripe's hosted checkout instead of building my own payment forms (so I never touch raw credit card data), and I ask Claude to review code specifically for security issues — "review this code for common security vulnerabilities, especially OWASP Top 10" — which catches a surprising amount.

But I am aware that what I do not know could hurt my users. If any of my products scaled to thousands of users, I would hire a security consultant for a proper audit. That is a cost I have budgeted for but not yet needed.

The Vibe Coder's Security Mindset

Security for a vibe coder is not about becoming a security expert. It is about three things:

Assume the AI missed something. Claude writes secure code most of the time. Most of the time is not good enough when the one time it misses could expose your users' data. Check the output. Ask it to review its own work for vulnerabilities. Add that extra layer of skepticism.

Use platforms that handle security for you. Supabase handles authentication, database security, and RLS. Stripe handles payments. Vercel handles deployment security. I chose these tools specifically because their security is maintained by teams of people smarter than me. Every piece of security I can delegate to a platform is a piece I do not have to get wrong.

Build the habit, not the expertise. I will never be a security expert. But I can check my seven-item list before every deploy. I can move keys to environment variables. I can enable RLS. These are habits, not skills. Anyone can build them.

The Stripe key on line 47 was a wake-up. I was building like someone who assumed the AI handled everything. It does not. The AI builds the house. You check the locks.

What is the security practice you know you should have but keep skipping — and what would it take to make it automatic?


I write about freedom, healing, and building alone. The full archive is at soulin.co.

Related vibe-coding posts