🥴 Vibe Coder's Guide

Practical tips, security checklists, and easy-to-follow recommendations for vibe coders to make your AI-generated apps more secure.

🔑 Authentication & Authorization

  • Use a trusted auth provider (Auth0, Clerk, Supabase Auth, etc.) instead of rolling your own
  • Always require strong passwords (min 12 chars, mixed case, numbers, symbols)
  • Implement proper session timeouts and refresh token rotation
  • Validate user permissions on both client AND server
  • Use JWTs but store them securely (HTTP-only cookies)

🛡️ Data Validation

  • Validate ALL user input on the server, even if validated on the client
  • Use a schema validation library (Zod, Yup, Joi, etc.)
  • Sanitize user input to prevent XSS attacks
  • Use parameterized queries to prevent SQL injection
  • Implement rate limiting on all API endpoints

🔍 API Security

  • Use HTTPS for all API endpoints (no exceptions!)
  • Implement proper CORS policies
  • Add security headers (Content-Security-Policy, X-Content-Type-Options, etc.)
  • Hide sensitive error details in production
  • Use API keys with least privilege principles

💾 Database Security

  • Encrypt sensitive data at rest
  • Use database roles with minimal permissions needed
  • Never expose your database directly to the internet
  • Back up your data regularly and test restores
  • Use prepared statements for all database queries

🔥 Quick Fixes for Common Vibe Coding Issues

Hard-coded Secrets

// ❌ BAD: Hard-coded API keys in your code
const apiKey = "sk_live_51KjD...";
// ✓ GOOD: Use environment variables
const apiKey = process.env.API_KEY;
// Make sure to add API_KEY to your .env file and .gitignore

Insecure Direct Object References

// ❌ BAD: Fetching data without authorization check
app.get('/api/users/:id', (req, res) => {
  const user = db.getUser(req.params.id);
  res.json(user);
});
// ✓ GOOD: Check that the user is authorized
app.get('/api/users/:id', (req, res) => {
  // Verify current user has permission to access this user
  if (req.user.id !== req.params.id && !req.user.isAdmin) {
    return res.status(403).json({ error: 'Not authorized' });
  }
  const user = db.getUser(req.params.id);
  res.json(user);
});

Cross-Site Scripting (XSS)

// ❌ BAD: Directly inserting HTML from user input
document.getElementById('content').innerHTML = userComment;
// ✓ GOOD: Sanitize user input or use framework escaping
import DOMPurify from 'dompurify';

// Option 1: Sanitize HTML if you need to allow some HTML
document.getElementById('content').innerHTML = 
  DOMPurify.sanitize(userComment);

// Option 2: Text content only (preferable if HTML not needed)
document.getElementById('content').textContent = userComment;

📚 Resources

Attribution

Information compiled from OWASP, Mozilla Web Security Guidelines, Node.js Security Best Practices, and Auth0 Documentation.