How to Secure a Next.js App Built with AI Coding Tools
OverMCP Team
TL;DR: To secure an AI-generated Next.js app, you must (1) check for hardcoded secrets, (2) validate all user inputs, (3) enforce proper authentication and authorization, (4) sanitize outputs to prevent XSS, (5) implement rate limiting and CSRF protection, and (6) run automated security scans regularly. This guide walks through each step with concrete code examples.
Why AI-Generated Next.js Apps Need Extra Security
AI coding tools like Cursor, Bolt.new, and v0 dramatically speed up development, but they often skip security best practices. Common issues include hardcoded API keys, missing input validation, weak authentication, and exposed internal routes. Without a security review, these apps can be easily exploited.
Step 1: Audit for Hardcoded Secrets and Exposed Environment Variables
AI tools sometimes generate code that hardcodes secrets or exposes .env files. Use a scanner or manually check for these patterns.
Example of insecure code:
// app/api/route.js
const apiKey = 'sk-...'; // Danger: hardcodedFix: Use environment variables and never expose them to the client:
// app/api/route.js
const apiKey = process.env.API_KEY;Also, ensure your .env file is in .gitignore and never commit it. Use a tool like OverMCP to scan for leaked secrets automatically.
Step 2: Validate and Sanitize All User Inputs
AI-generated forms often skip server-side validation. Always validate inputs on the server.
Example of insecure code:
// app/api/user/route.js
export async function POST(request) {
const data = await request.json();
// No validation
await db.user.create({ data });
}Fix with Zod:
import { z } from 'zod';
const userSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(100),
});
export async function POST(request) {
const data = await request.json();
const parsed = userSchema.safeParse(data);
if (!parsed.success) {
return new Response('Invalid input', { status: 400 });
}
await db.user.create({ data: parsed.data });
}Step 3: Secure Authentication and Authorization
AI tools may generate basic session management that is vulnerable to session fixation or token theft.
Use NextAuth.js with best practices:
// app/api/auth/[...nextauth]/route.js
import NextAuth from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
export const authOptions = {
providers: [
CredentialsProvider({
async authorize(credentials) {
// Validate credentials securely
},
}),
],
session: {
strategy: 'jwt',
maxAge: 60 * 60, // 1 hour
},
callbacks: {
async jwt({ token, user }) {
// Add user ID to token
if (user) token.id = user.id;
return token;
},
},
};
export default NextAuth(authOptions);Protect API routes:
// app/api/protected/route.js
import { getServerSession } from 'next-auth';
import { authOptions } from '../auth/[...nextauth]/route';
export async function GET() {
const session = await getServerSession(authOptions);
if (!session) {
return new Response('Unauthorized', { status: 401 });
}
return new Response('Protected data');
}Step 4: Prevent Cross-Site Scripting (XSS)
AI-generated content rendering can introduce XSS if you use dangerouslySetInnerHTML without sanitization.
Insecure:
<div dangerouslySetInnerHTML={{ __html: userContent }} />Fix with DOMPurify:
import DOMPurify from 'isomorphic-dompurify';
const sanitizedContent = DOMPurify.sanitize(userContent);
return <div dangerouslySetInnerHTML={{ __html: sanitizedContent }} />;Step 5: Implement Rate Limiting and CSRF Protection
Next.js API routes are often exposed without rate limiting. Use a middleware or external service.
Example with Upstash Ratelimit:
// app/api/contact/route.js
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, '10 s'),
});
export async function POST(request) {
const ip = request.headers.get('x-forwarded-for') || 'unknown';
const { success } = await ratelimit.limit(ip);
if (!success) {
return new Response('Too many requests', { status: 429 });
}
// handle request
}Step 6: Run Automated Security Scans
Manual checks are not enough. Use a dedicated security scanner designed for vibe-coded apps. OverMCP automatically scans your Next.js app for hardcoded secrets, exposed endpoints, missing headers, and common CVEs. Integrate it into your CI/CD pipeline to catch issues before deployment.
Conclusion
Securing an AI-generated Next.js app requires a systematic approach: audit secrets, validate inputs, enforce authentication, sanitize outputs, rate-limit endpoints, and scan automatically. By following these steps, you can ship fast and stay safe.
FAQ
What is the most common security issue in AI-generated Next.js apps?
Hardcoded secrets and API keys in client-side code are the most common issues. AI tools often inline tokens without using environment variables.
How do I find security vulnerabilities in my Next.js app built with AI?
Use automated scanners like OverMCP that check for leaked secrets, missing authentication, exposed routes, and other vulnerabilities. Also review your code manually using the steps in this guide.
Is Next.js itself secure by default?
Next.js provides some built-in protections like automatic XSS escaping, but developers must still implement authentication, authorization, input validation, and rate limiting. AI-generated code often skips these.