Group
Security2 min read

The CSRF Bug Every AI Codegen Tool Ships (Part 16)

Cross-site request forgery is boring, well-understood, and shipped by default in almost every AI-generated app we see. Here is how it happens.

Cross-site request forgery is the security bug of the 2000s. It is well-understood. It is easy to fix. There are entire libraries whose only job is to fix it for you. And yet, in 2026, we audit AI-generated apps and find it by default, in nearly every project.

Here's the pattern.

The setup

The AI generates an app with:

  • Session cookies for auth (Set-Cookie: session=...; HttpOnly)
  • A REST API at /api/* that reads those cookies
  • No CSRF token
  • No SameSite cookie attribute (or SameSite=None)
  • CORS configured to allow the frontend origin

Everything works. The user logs in, the cookie is set, the app reads it on every request. Ship it.

The attack

An attacker sends the user a link to https://cool-cat-pictures.example. That page contains:

<form action="https://your-app.example/api/account/delete" method="POST">
  <input type="hidden" name="confirm" value="yes" />
</form>
<script>document.forms[0].submit();</script>

The user's browser cheerfully sends the request to your app with their session cookie attached, because that's what browsers do. Your app sees an authenticated request with the right cookie and deletes the account.

The user never clicked anything on your site. They clicked a link on someone else's site. Your app authenticated them, and then executed an action they never intended.

Why the AI ships it

Because it works in development. The bug is invisible until a hostile third-party site is involved. Every LLM's training data is full of API examples where nobody thought about the attacker.

The fix, in order of preference

1. SameSite=Lax cookies

The cheapest fix in web history:

response.cookie('session', session_id, {
  httpOnly: true,
  secure: true,
  sameSite: 'lax',
});

SameSite=Lax tells the browser: "don't send this cookie on cross-site POST requests." That single attribute kills 95% of practical CSRF attacks. It has been supported by every browser since 2020.

2. CSRF tokens for anything sensitive

For state-changing endpoints, issue a per-session CSRF token, put it in a header on requests from your frontend, and verify it server-side:

const verify_csrf = (request) => {
  const header_token = request.headers['x-csrf-token'];
  const session_token = request.session?.csrf_token;

  if (!header_token || header_token !== session_token) {
    throw { status: 403, message: 'Bad CSRF token.' };
  }
};

The attacker's page can trigger a request, but it can't read your session's token to include, because same-origin policy blocks it.

3. Origin / Referer checking

For endpoints that must accept cross-origin requests (webhooks, etc.), verify the Origin header explicitly against an allowlist. Never trust the Origin header for auth — trust it only to reject mismatches.


None of this is new. All of it is easy. The next audit you get, ask the auditor to find your CSRF story. If they can't find one, that is the story.

Get an expert audit of your vibe coded app

Don't go live without letting an expert set of eyes review your code. Fortivibe runs 173 checks against your repo, reviewing things like security, stability, and user experience.

Get Your App Audited Now

Team Fortivibe Updated 111 days ago

Copy Link