← All articles

Get new article notifications.

How Secure is Your Vibe-Coded App?

If you're feeling unstoppable after a vibe-coding session, do this before you do anything else: ask Claude for a full security audit of what it just built. Then tell it to fix everything it found. Rinse and repeat until the audit comes back clean.

It works because the AI reviews your code against a checklist it knows cold — SQL injection, XSS, CSRF, session handling, credential storage, access control. It catches stuff you'd never think to look for, which matters most if you didn't come up through traditional development.

Security researchers have found that roughly 45% of AI-generated code contains vulnerabilities, including command injection, hardcoded secrets, and SQL injection flaws (DXTalks).

To show you what that actually looks like, here are ten real findings from audits Claude ran on my own apps — the severity it assigned, and how each one got fixed. This is one audit pass.

High severity

  • Account passwords stored in plaintext. One app encrypted its vault entries with AES-256-GCM but stored a separate accounts table's passwords as raw text. One database dump and every password is readable. Fix: encrypt those the same way as the vault, with a backward-compatible helper and a one-time migration for the existing rows.
  • Auth token sitting in browser localStorage. A "remember me" token was being written to localStorage, where any XSS or sketchy browser extension can grab it. Fix: move it into an httpOnly, Secure, SameSite=Strict cookie so JavaScript can't see it at all.
  • Plaintext password fallback in a login check. A builder app checked the password against a freshly hashed value — but also had a fallback comparison straight against the plaintext password in config.php. Anyone who could read that file had the password. Fix: store a pre-hashed password and verify against it with one password_verify() call.

Medium severity

  • Subscription bypass through Stripe parameters. A success page activated an account from a subscription ID in the URL without checking the subscription belonged to the logged-in user. Anyone with any valid Stripe subscription ID could turn their account on for free. Fix: confirm the subscription's customer matches the logged-in user's Stripe customer ID before activating.
  • Session fixation window. The login flow called session_regenerate_id(false). The false means the old session file never gets deleted, so a pre-login session ID stays alive. Fix: change it to true. One character.
  • Change-password didn't check the current password. The endpoint only checked the new password's length. A hijacked session could change the password and lock the real owner out. Fix: require the current password and verify it server-side before allowing the change.
  • XSS in the confirmation-modal system. PHP escaped a filename correctly, but the JavaScript modal read it back out of a data attribute — which the browser HTML-decodes — and dropped it into innerHTML. A file named with an onerror payload runs the second a moderator clicks it. Fix: set the modal's text with textContent, not innerHTML.
  • Missing CSRF check on an admin action. An admin "view as another user" endpoint skipped CSRF verification, so a forged request could make an admin open someone else's data. Fix: add the same verifyCsrf() call every other state-changing action already had.

Low severity

  • No CSRF token on the login form. This lets an attacker quietly log the victim into the attacker's account, so the victim ends up entering their data into it. Low impact, but real. Fix: add a CSRF token to the login form like every other form.
  • HTTPS not forced. The HTTP-to-HTTPS redirect in .htaccess was commented out. Session cookies only get flagged Secure when HTTPS is detected, so a plain HTTP request ships the cookie in the clear. Fix: uncomment the redirect.

How to write an effective AI prompt

"Run a security audit" is too vague to be useful. Asked that way, an assistant produces a tidy report whether or not anything is wrong, and if you cannot read the code you have no way to tell those two outcomes apart. These prompts ask for a file name, a line number, or the output of a command, so the answer either contains evidence or it does not.

Give these two instructions first, because they change the quality of everything that follows.

You wrote this code. Review it now as though a hostile stranger wrote it and you are being paid to find flaws. Do not defend earlier decisions.
For every issue, give me the file, the line, and the exact input that triggers it. If you cannot produce a triggering input, label that finding unverified.

Ask these first

The ones that lose data or hand over an account.

  1. Secrets. "List every file containing a password, API key, token, or database connection string. For each, tell me whether it is committed to git, and show me the output of git log --all -- followed by the file name."
  2. Who can reach what. "List every page or route in this project. For each, tell me exactly what stops a logged-out stranger loading it. Where the answer is nothing, say nothing."
  3. Passwords. "Show me every line where a password is saved or compared. Paste the lines. Is any comparison made against a plaintext value?"
  4. Database queries. "Show me every query built by joining strings with variables instead of using parameters."
  5. Uploads. "If users can upload a file, show me what stops someone uploading a program and then opening it in a browser."
  6. Proof. "Do not tell me whether this is safe. Write a script that attacks it, run the script, and show me the output."

The last one earns its place. A check in my own code had been written so that it passed when both values were empty, which is what happens on a first visit, so it rejected nothing. The function used was the right one and the line reads correctly. Four applications carried that bug and no amount of reading found it. Running an attack against the endpoint found it in seconds.

Then these

The ones that help an attacker who is already poking around.

  1. Login attempts. "How many times can someone guess a password before being blocked? Show me the code that enforces the limit."
  2. Cookies. "Show me where session cookies are configured. Are HttpOnly, Secure and SameSite all set?"
  3. Password resets. "Walk me through the reset flow. How long is the link valid, can it be used twice, and what stops someone requesting a reset for an address they do not own?"
  4. Error messages. "When something breaks in production, what does a visitor see? Show me where errors are displayed and where they are logged."
  5. Logs. "Does anything write a password, token or card number into a log file? Show me every line that logs a request body."
  6. Dependencies. "List every third-party package with its version and release date. Flag any unmaintained or more than two years old."

Lower risk, still worth closing

These are unlikely to be the thing that gets you.

  1. Files served by accident. "List every file a visitor can download by typing its name in the address bar. Include notes, backups, and anything ending in .md, .sql, .bak or .zip."
  2. Security headers. "Which security response headers are set, and which common ones are missing?"
  3. What is listening. "What network ports does this open, and what is each one for?"
  4. Backups. "If the database were deleted right now, what would I restore from, and when was that last tested?"

If it runs on your laptop instead of a website

A Python script on your own machine is not exposed to the internet, so most of the list above does not apply. Four questions still do.

  1. "What does this send over the network, and where to? List every address it contacts."
  2. "What files can this delete or overwrite? Show me every write outside its own folder."
  3. "Are any credentials stored in plain text here, and would they be included if I shared the folder?"
  4. "List every package this installs and what each is for. Flag any a program like this would not need."

Always ask AI to check its work

What did you not check? List the parts of this project you did not review, and the categories of problem you did not look for.

A clean audit usually means a narrow review rather than sound code. Asking what was skipped turns that into a map of where to look next, and it is the one question an assistant will never raise on its own.

These security issues are the same handful of mistakes showing up app after app — credential storage, session handling, output escaping, access control. That's exactly why an audit is critical: the failure modes are predictable, and an AI that's seen thousands of them finds yours fast.

But the audit is only as good as your ability to read the results and judge them. Severity is a judgment call. "Encrypt those passwords" can be done a dozen right ways and a dozen wrong ones. If you've never lived the full software development life cycle, you can run the audit — but you won't always know which findings to lean on or whether the fix actually fixed anything. It's a force multiplier, not a substitute for knowing what you're looking at.

UGREEN Summer Sale: Up to 45% Off, Spend & Save

Posts on this site contain affiliate links. As an Amazon Associate and member of other affiliate programs, I earn from qualifying purchases at no extra cost to you.

Leave a comment

Comments are moderated — I read every one. Your email is only used if I need to follow up; it isn't published. Links aren't allowed.

← All articles