8 essential security practices for vibe coding. Protect your AI-generated applications without slowing down your development speed.
See which practices you're missing.
Configure Row Level Security (Supabase) or Security Rules (Firebase) before adding any data to your database.
AI creates tables without security by default. Once you have data, it's exposed until you add protection.
-- Supabase RLS Example ALTER TABLE user_profiles ENABLE ROW LEVEL SECURITY; CREATE POLICY "Users can view own profile" ON user_profiles FOR SELECT TO authenticated USING ((select auth.uid()) = user_id); CREATE POLICY "Users can update own profile" ON user_profiles FOR UPDATE TO authenticated USING ((select auth.uid()) = user_id);
Creating tables and adding data before configuring RLS policies
Never hardcode API keys, database passwords, or any credentials in your source code.
AI often hardcodes keys for quick functionality. These get committed to git and exposed publicly.
// ❌ Don't do this
const stripe = new Stripe('sk_live_abc123...');
// ✅ Do this instead
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
// .env.local (never commit this file)
STRIPE_SECRET_KEY=sk_live_abc123...Committing .env files or hardcoded keys to git repositories
Verify authentication on every API endpoint, not just in the frontend UI.
AI generates client-side checks that hide features but don't actually protect the underlying APIs.
// Next.js API Route Example
import { createClient } from '@/lib/supabase/server';
export async function GET(request: Request) {
const supabase = await createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
// Now safe to fetch user's data
const { data } = await supabase
.from('profiles')
.select()
.eq('user_id', user.id);
return Response.json(data);
}Only checking authentication in React components, not API routes
Never trust data from users. Validate format, sanitize content, and use parameterized queries.
AI often skips input validation for simplicity, leaving apps vulnerable to injection attacks.
import { z } from 'zod';
// Define expected input schema
const UserInputSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(100),
age: z.number().int().positive().max(150),
});
// Validate before processing
export async function POST(request: Request) {
const body = await request.json();
const result = UserInputSchema.safeParse(body);
if (!result.success) {
return Response.json({ error: result.error }, { status: 400 });
}
// result.data is now typed and validated
await db.insert(users).values(result.data);
}Directly inserting user input into database queries without validation
Add HTTP security headers to protect against XSS, clickjacking, and other client-side attacks.
AI generates application code but doesn't configure infrastructure-level security headers.
// next.config.js
const securityHeaders = [
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-XSS-Protection', value: '1; mode=block' },
{ key: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubDomains' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
];
module.exports = {
async headers() {
return [{ source: '/(.*)', headers: securityHeaders }];
},
};Deploying without any security headers configured
Require email verification, strong passwords, and consider multi-factor authentication.
AI uses default auth settings that accept weak passwords and skip email verification.
// Password validation before signup
function validatePassword(password: string): boolean {
const minLength = 12;
const hasUppercase = /[A-Z]/.test(password);
const hasLowercase = /[a-z]/.test(password);
const hasNumber = /[0-9]/.test(password);
const hasSpecial = /[!@#$%^&*]/.test(password);
return (
password.length >= minLength &&
hasUppercase &&
hasLowercase &&
hasNumber &&
hasSpecial
);
}
// In Supabase dashboard: Enable email confirmation
// Authentication > Settings > Enable email confirmationsAllowing '123456' as a valid password
Verify users can only access data they own. Don't just check if they're logged in.
AI writes clean APIs but forgets to verify the user owns the specific resource they're accessing.
// ❌ Vulnerable - only checks authentication
const order = await db.orders.findById(orderId);
return order;
// ✅ Secure - checks ownership
const order = await db.orders.findFirst({
where: {
id: orderId,
userId: session.user.id // Only returns if user owns it
}
});
if (!order) {
throw new Error('Order not found'); // Same error for not found vs not owned
}
return order;Returning data based on ID without checking the user owns that resource
Automate security scanning in your development workflow to catch issues before they reach production.
AI introduces new vulnerabilities with each code generation. Continuous scanning catches them early.
# Example: Add to deployment checklist Pre-deployment security checklist: □ Run VAS security scan □ Review and fix critical findings □ Verify RLS policies are enabled □ Confirm no secrets in codebase □ Test auth flows work correctly □ Check security headers in response
Deploying AI-generated code without any security review
VAS scans your vibe-coded app and tells you which best practices you're following and which need attention.
Run Free Security ScanEnabling database access controls (RLS/Security Rules) is the most critical practice. Without it, anyone can read, modify, or delete all data in your database. This is the #1 cause of data breaches in vibe-coded applications.
Yes, treat AI output as a first draft that needs security review. Focus on: database configurations, authentication logic, API endpoints, and anywhere secrets might be hardcoded. You don't need to review every line, but security-critical sections need human oversight.
Run an automated security scan to identify vulnerabilities. Check that: RLS is enabled on all tables, no secrets are in the codebase, all API routes verify authentication, security headers are configured, and email verification is enabled.
Yes. Establish secure defaults: always enable RLS when creating tables, use environment variables from the start, add auth checks to API route templates. These practices add minimal time but prevent major security issues.
Use VAS for scanning vibe-coded apps specifically. Also consider: Zod for input validation, your hosting platform's security features, and your auth provider's built-in security settings. These tools catch most common AI-generated vulnerabilities.
Last updated: January 16, 2026