Security Operations & Deployment
When to Apply Each Middleware - Decision Guide
withRateLimit() - Apply to:
✅ Always apply to:
- •Any route that could be abused (spam, brute force)
- •Login-like operations (even if Clerk handles auth)
- •Data creation/modification endpoints
- •Contact/support form endpoints
- •Webhooks (to prevent DoS)
- •File upload endpoints
- •Search endpoints
- •Data export endpoints
- •Any expensive AI/API operations
- •Report generation
- •Bulk operations
❌ Usually not needed for:
- •Static asset requests (handled by CDN)
- •Simple GET endpoints that only read public data
- •Health check endpoints
- •Endpoints already protected by authentication rate limits
withCsrf() - Apply to:
✅ Always apply to:
- •All POST/PUT/DELETE operations
- •Any state-changing operation
- •Form submissions
- •Account modifications
- •Payment operations
- •Data deletion operations
❌ Skip for:
- •GET requests (read-only operations)
- •Public read-only endpoints
- •Webhooks (use signature verification instead)
Combining Both Middlewares
For maximum protection:
// Order matters: rate limit first, then CSRF export const POST = withRateLimit(withCsrf(handler));
Why order matters:
- •Rate limiting runs first to block excessive requests early
- •CSRF verification runs on requests that pass rate limiting
- •More efficient: don't waste CSRF verification on rate-limited requests
Decision Matrix:
| Route Type | Rate Limit | CSRF | Authentication |
|---|---|---|---|
| Public form submission | ✅ Yes | ✅ Yes | ❌ No |
| Protected data modification | ✅ Yes | ✅ Yes | ✅ Yes |
| Public read-only API | ❌ No | ❌ No | ❌ No |
| Protected read-only API | ✅ Maybe | ❌ No | ✅ Yes |
| Webhook endpoint | ✅ Yes | ❌ No | ✅ Signature |
| File upload | ✅ Yes | ✅ Yes | ✅ Yes |
Environment Variables & Secrets
Required Environment Variables for This Project
Development (.env.local - NEVER commit):
# CSRF Protection
# Generate with: node -p "require('crypto').randomBytes(32).toString('base64url')"
CSRF_SECRET=<32-byte-base64url-string>
SESSION_SECRET=<32-byte-base64url-string>
# Clerk Authentication (from Clerk dashboard)
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...
NEXT_PUBLIC_CLERK_FRONTEND_API_URL=https://your-app.clerk.accounts.dev
# Convex Database (from Convex dashboard)
CONVEX_DEPLOYMENT=dev:...
NEXT_PUBLIC_CONVEX_URL=https://...convex.cloud
# Optional: Stripe (if using direct Stripe, not Clerk Billing)
STRIPE_SECRET_KEY=sk_test_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
# Optional: Clerk Webhook Secret
CLERK_WEBHOOK_SECRET=whsec_...
Production (Vercel/hosting platform):
# CSRF Protection (different from dev!) CSRF_SECRET=<different-32-byte-string> SESSION_SECRET=<different-32-byte-string> # Clerk Production Keys NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_... CLERK_SECRET_KEY=sk_live_... NEXT_PUBLIC_CLERK_FRONTEND_API_URL=https://your-app.clerk.accounts.com # Convex Production CONVEX_DEPLOYMENT=prod:... NEXT_PUBLIC_CONVEX_URL=https://...convex.cloud # Optional: Stripe Production STRIPE_SECRET_KEY=sk_live_... NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_... # Optional: Clerk Webhook Secret (production) CLERK_WEBHOOK_SECRET=whsec_...
Generating Secrets
# Generate CSRF_SECRET (32 bytes)
node -p "require('crypto').randomBytes(32).toString('base64url')"
# Generate SESSION_SECRET (32 bytes)
node -p "require('crypto').randomBytes(32).toString('base64url')"
Environment Variable Best Practices
✅ DO:
- •Use different secrets for dev/staging/production
- •Generate strong random secrets (32+ bytes)
- •Add
.env.localto.gitignore - •Store production secrets in hosting platform's secret manager
- •Rotate secrets quarterly
- •Validate required environment variables on startup
❌ NEVER:
- •Hardcode API keys, tokens, or secrets in code
- •Commit
.env.localto version control - •Log environment variables
- •Expose secrets in client-side code
- •Use
.env.localvalues inNEXT_PUBLIC_*variables (they're exposed to browser!) - •Share secrets via email, Slack, or insecure channels
Validating Configuration on Startup
// lib/config.ts
const requiredEnvVars = [
'CSRF_SECRET',
'SESSION_SECRET',
'NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY',
'CLERK_SECRET_KEY',
'NEXT_PUBLIC_CONVEX_URL'
];
export function validateConfig() {
const missing = requiredEnvVars.filter(v => !process.env[v]);
if (missing.length > 0) {
throw new Error(`Missing required environment variables: ${missing.join(', ')}`);
}
// Validate secret lengths
if (process.env.CSRF_SECRET && process.env.CSRF_SECRET.length < 32) {
throw new Error('CSRF_SECRET must be at least 32 characters');
}
if (process.env.SESSION_SECRET && process.env.SESSION_SECRET.length < 32) {
throw new Error('SESSION_SECRET must be at least 32 characters');
}
}
// In your app startup (e.g., middleware.ts or layout.tsx)
validateConfig();
Pre-Deployment Security Checklist
Run through this checklist before every production deployment:
Environment & Configuration
- • All environment variables set in production environment
- •
CSRF_SECRETgenerated and configured (32+ bytes) - •
SESSION_SECRETgenerated and configured (32+ bytes) - • Clerk production keys configured (
pk_live_...,sk_live_...) - • Convex production deployment configured
- • Stripe live mode keys configured (if using direct Stripe)
- •
.env.localNOT committed to git (check withgit status) - • Different secrets used for dev vs production
Dependencies
- • Run
npm audit --production- 0 vulnerabilities - • Run
npm outdated- Check for critical security updates - •
package-lock.jsoncommitted to git - • Next.js on latest stable version (currently 15.5.4+)
- • All critical packages updated
Security Features
- • CSRF protection tested (see
security-testingskill) - • Rate limiting tested (
node scripts/test-rate-limit.js) - • Input validation tested with malicious input
- • Security headers verified (
curl -I https://yourapp.com) - • HSTS enabled in production (automatic in middleware)
- • Error messages are generic in production (no stack traces)
Authentication & Authorization
- • Protected routes require authentication
- • Resource ownership checked before access
- • Subscription status verified for premium features
- • Webhook signatures verified (Clerk, Stripe)
- • Session expiration handled gracefully
- • No hardcoded credentials in code
API Security
- • All POST/PUT/DELETE routes have CSRF protection
- • All public endpoints have rate limiting
- • All user input validated with Zod schemas
- • All errors handled with error handler utilities
- • No sensitive data in logs (passwords, tokens, cards, PII)
- • No hardcoded secrets in code (grep check below)
Payment Security (if applicable)
- • Using Clerk Billing + Stripe (not handling cards directly)
- • Webhooks verified with Svix signatures
- • Subscription status checked on server
- • Test mode disabled in production
- • No card data logged anywhere
Testing
- • Rate limit test passes:
node scripts/test-rate-limit.js - • CSRF protection tested manually
- • Input validation tested with XSS payloads
- • Security headers checked:
curl -I https://yourapp.com - • Authentication flows tested
- • Error handling tested in production mode
Final Checks
# Check for hardcoded secrets grep -r "sk_live" . --exclude-dir=node_modules grep -r "AKIA" . --exclude-dir=node_modules grep -r "api_key.*=" . --exclude-dir=node_modules # Verify .env.local not in git git status | grep .env.local # Should return nothing # Run full security audit npm audit --production bash scripts/security-check.sh # Test production build npm run build NODE_ENV=production npm start
Security Monitoring Post-Deployment
What to Monitor
Server Logs (Daily)
Monitor for these patterns that indicate potential attacks:
Rate Limit Violations (HTTP 429):
- Repeated 429 errors from same IP → potential abuse/brute force - High volume of 429s → possible distributed attack - 429s on login endpoints → credential stuffing attempt
CSRF Failures (HTTP 403):
- Repeated 403 with "CSRF token invalid" → potential CSRF attack - Sudden spike in CSRF failures → possible automated attack - 403s without prior token fetch → attack bypass attempt
Authentication Failures (HTTP 401/403):
- 401 spikes → potential brute force on protected endpoints - 403 spikes → unauthorized access attempts - Pattern of 401 followed by 403 → enumeration attack
Unusual Error Patterns:
- Sudden increase in 500 errors → potential attack or system issue - 400 errors with validation failures → input attack attempts - Errors from unusual geographic locations
Metrics to Track (Weekly)
Authentication Metrics:
- •Failed authentication attempts per hour
- •Account lockouts (if implemented)
- •Geographic distribution of login attempts
- •Unusual login times (3am mass logins = bot)
Rate Limiting Metrics:
- •Rate limit violations per IP
- •Top IPs hitting rate limits
- •Endpoints most frequently rate-limited
- •Rate limit violation trends over time
CSRF Protection Metrics:
- •CSRF validation failures
- •CSRF token generation rate
- •Token reuse attempts
- •Missing token attempts
Input Validation Metrics:
- •Validation failures by field
- •XSS attempt patterns (script tags in input)
- •SQL injection attempt patterns
- •Excessive input length attempts
Error Rate Metrics:
- •Error rates by endpoint
- •Error rates by HTTP status code
- •Error rate trends over time
- •Geographic distribution of errors
Setting Up Monitoring
Vercel Logs (Built-in)
# View logs in Vercel dashboard https://vercel.com/your-project/logs # Filter by status code Status: 429 # Rate limited Status: 403 # CSRF/Forbidden Status: 401 # Unauthorized
Clerk Dashboard (Authentication)
Monitor in Clerk dashboard:
- •Failed sign-in attempts
- •Account creation rate
- •Session activity
- •Suspicious IP addresses
Custom Logging
// lib/security-logger.ts
export function logSecurityEvent(event: {
type: 'RATE_LIMIT' | 'CSRF_FAILURE' | 'AUTH_FAILURE' | 'VALIDATION_FAILURE';
ip?: string;
userId?: string;
endpoint?: string;
details?: Record<string, any>;
}) {
const log = {
timestamp: new Date().toISOString(),
environment: process.env.NODE_ENV,
...event
};
// In production, send to logging service
if (process.env.NODE_ENV === 'production') {
console.log(JSON.stringify(log));
// Optional: Send to external service (Datadog, LogRocket, etc.)
} else {
console.log('Security Event:', log);
}
}
// Usage in middleware/routes
if (rateLimitExceeded) {
logSecurityEvent({
type: 'RATE_LIMIT',
ip: clientIp,
endpoint: request.nextUrl.pathname
});
}
Response Procedures
High-Priority Alerts (Immediate Response):
- •Massive spike in failed authentication (>100/min)
- •CSRF failures from many IPs (coordinated attack)
- •Sudden 500 error rate increase (>10x normal)
- •Known vulnerability being exploited
Medium-Priority (24-hour Response):
- •Gradual increase in rate limit violations
- •Single IP with persistent failed auth attempts
- •New error patterns in logs
- •Unusual traffic from new geographic regions
Low-Priority (Weekly Review):
- •Normal background failed auth attempts
- •Occasional rate limit hits
- •Standard input validation failures
- •Routine error patterns
Automated Alerting
Set up alerts in your hosting platform:
Vercel:
Alerts → New Alert Rule - Error rate > 10% for 5 minutes → Email/Slack - 429 responses > 100/min → Email/Slack - 500 responses > 50/min → Email/Slack
Custom Alerts:
// Monitor and alert on patterns
if (rateLimitViolations > THRESHOLD) {
await sendAlert({
severity: 'HIGH',
message: `Rate limit violations: ${rateLimitViolations}/min`,
ip: attackerIp
});
}
Resources & Documentation
Project Security Documentation
Implementation Guides:
- •
.claude/skills/security/security-overview/SKILL.md- Overall architecture - •
.claude/skills/security/*/SKILL.md- Individual security features - •
docs/security/SECURITY_IMPLEMENTATION.md- Complete implementation guide - •
README.md- Security Configuration section
Awareness & Learning:
- •
.claude/skills/security/security-awareness/- AI code vulnerability analysis - •
.claude/skills/security/security-awareness/awareness-overview/- Complete security overview
Testing & Verification Scripts
Security Testing:
- •
scripts/test-rate-limit.js- Rate limiting verification - •
scripts/security-check.sh- Dependency audit - •
scripts/security-test.sh- Comprehensive security test suite (if created)
Example Implementations:
- •
app/api/example-protected/route.ts- Complete security stack example - •
app/api/test-rate-limit/route.ts- Rate limiting test endpoint - •
app/api/csrf/route.ts- CSRF token generation
External Security Resources
OWASP (Security Standards):
- •OWASP Top 10 2021: https://owasp.org/www-project-top-ten/
- •OWASP Cheat Sheet Series: https://cheatsheetseries.owasp.org
- •OWASP API Security Top 10: https://owasp.org/www-project-api-security/
Framework & Service Docs:
- •Next.js Security: https://nextjs.org/docs/app/guides/security
- •Clerk Security: https://clerk.com/docs/security
- •Convex Security: https://docs.convex.dev/production/hosting/authentication
- •Stripe Security: https://stripe.com/docs/security
Testing Tools:
- •Security Headers Scanner: https://securityheaders.com/
- •Mozilla Observatory: https://observatory.mozilla.org/
- •SSL Labs Test: https://www.ssllabs.com/ssltest/
Maintenance Schedule
Daily
- •Check error logs in Vercel dashboard
- •Monitor Clerk dashboard for failed auth attempts
- •Review any security alerts
Weekly
- •Run
npm audit --production - •Check GitHub Dependabot alerts
- •Review error logs for patterns
- •Check rate limit violation trends
Monthly
- •Full security audit:
bash scripts/security-check.sh - •Update dependencies:
npm update+ test - •Review and rotate any compromised secrets
- •Re-run security testing suite
- •Check security headers: https://securityheaders.com/
Quarterly
- •Rotate CSRF_SECRET and SESSION_SECRET
- •Major framework updates (Next.js, React)
- •Full penetration test (manual XSS, CSRF, auth bypass attempts)
- •Review and update security policies
- •Security awareness training (review skills)
Quick Reference Commands
# Generate secrets
node -p "require('crypto').randomBytes(32).toString('base64url')"
# Check for vulnerabilities
npm audit --production
# Check for outdated packages
npm outdated
# Run security test suite
node scripts/test-rate-limit.js
bash scripts/security-check.sh
# Check for hardcoded secrets
grep -r "sk_live" . --exclude-dir=node_modules
grep -r "AKIA" . --exclude-dir=node_modules
# Test security headers
curl -I https://yourapp.com
# Verify .env.local not committed
git status | grep .env.local
# Production build test
npm run build
NODE_ENV=production npm start
Summary: Security Operations Principles
🔒 Before Deployment:
- •Checklist must be 100% complete
- •0 npm audit vulnerabilities
- •All tests passing
- •All environment variables configured
🔒 After Deployment:
- •Monitor logs daily
- •Respond to alerts immediately
- •Review metrics weekly
- •Update dependencies monthly
🔒 Continuous:
- •Security is never "done"
- •Stay updated on new vulnerabilities
- •Keep dependencies current
- •Test security features regularly
For implementation details, refer to individual security skills. For vulnerability awareness, refer to security-awareness skills.