How to Fix Missing CSRF Protection in AI-Coded Next.js Apps
OverMCP Team
Quick answer
CSRF protection is often missing in AI-coded Next.js apps because AI tools generate minimal server actions and API routes without anti-forgery tokens. To fix it, add a CSRF token using middleware or a library like csrf-csrf, and validate it on every state-changing request. This prevents attackers from tricking authenticated users into executing unwanted actions.
What to check first
Before adding CSRF protection, verify your app's current state:
If you answered "no" to any of these, your app is vulnerable.
Step-by-step fix
1. Install a CSRF library
For Next.js App Router, a lightweight option is csrf-csrf:
npm install csrf-csrf2. Create a CSRF middleware
In src/middleware.ts, generate and validate tokens:
import { NextRequest, NextResponse } from 'next/server';
import { createCsrfMiddleware } from 'csrf-csrf';
const { csrfMiddleware, csrfToken } = createCsrfMiddleware({
cookie: { name: 'csrf-token', sameSite: 'strict' },
header: 'x-csrf-token',
});
export function middleware(request: NextRequest) {
// Protect all API routes and server actions
if (request.nextUrl.pathname.startsWith('/api') || request.method !== 'GET') {
return csrfMiddleware(request, NextResponse.next());
}
return NextResponse.next();
}
export const config = {
matcher: ['/api/:path*', '/action/:path*'],
};3. Add CSRF token to your forms
In a server component or client component, fetch the token from a dedicated endpoint or pass it via props. Example using a simple API route:
// app/api/csrf/route.ts
import { csrfToken } from '@/middleware';
import { NextRequest } from 'next/server';
export async function GET(request: NextRequest) {
const token = await csrfToken(request);
return Response.json({ token });
}Then in your form:
// app/components/MyForm.tsx
'use client';
import { useEffect, useState } from 'react';
export default function MyForm() {
const [csrfToken, setCsrfToken] = useState('');
useEffect(() => {
fetch('/api/csrf')
.then(res => res.json())
.then(data => setCsrfToken(data.token));
}, []);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);
const res = await fetch('/api/submit', {
method: 'POST',
headers: { 'x-csrf-token': csrfToken },
body: formData,
});
};
return (
<form onSubmit={handleSubmit}>
<input type="hidden" name="csrf_token" value={csrfToken} />
{/* your fields */}
<button type="submit">Submit</button>
</form>
);
}4. Validate on the server
// app/api/submit/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { csrfToken } from '@/middleware';
export async function POST(request: NextRequest) {
try {
await csrfToken(request); // throws if invalid
// process form...
return NextResponse.json({ success: true });
} catch {
return NextResponse.json({ error: 'Invalid CSRF token' }, { status: 403 });
}
}Common mistakes
FAQ
What is CSRF and why is it dangerous?
CSRF (Cross-Site Request Forgery) tricks an authenticated user into unknowingly executing actions on your app. For example, an attacker could create a malicious link that changes the user's email or password. In vibe-coded apps, missing CSRF protection is common because AI tools don't add it by default.
Does Next.js have built-in CSRF protection?
Next.js does not provide built-in CSRF protection for API routes or server actions. You must implement it yourself using middleware or a library. The App Router's server actions are particularly vulnerable because they can be called from any origin without a token.
Can I use SameSite cookies instead of CSRF tokens?
SameSite cookies (especially Strict or Lax) reduce CSRF risk but are not a complete replacement. They don't protect against attacks from subdomains or browser extensions. A CSRF token provides a second layer of defense that validates the request's authenticity.