// Operational security guidance for deployment, monitoring, and maintenance. Use this skill when you need to understand which middlewares to apply, configure environment variables, monitor security post-deployment, or follow the pre-deployment checklist. Triggers include "security operations", "deployment security", "security monitoring", "environment variables", "when to use middleware", "pre-deployment", "security checklist", "production security".
| name | security-operations-deployment |
| description | Operational security guidance for deployment, monitoring, and maintenance. Use this skill when you need to understand which middlewares to apply, configure environment variables, monitor security post-deployment, or follow the pre-deployment checklist. Triggers include "security operations", "deployment security", "security monitoring", "environment variables", "when to use middleware", "pre-deployment", "security checklist", "production security". |
✅ Always apply to:
❌ Usually not needed for:
✅ Always apply to:
❌ Skip for:
For maximum protection:
// Order matters: rate limit first, then CSRF
export const POST = withRateLimit(withCsrf(handler));
Why order matters:
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 |
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_...
# 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')"
✅ DO:
.env.local to .gitignore❌ NEVER:
.env.local to version control.env.local values in NEXT_PUBLIC_* variables (they're exposed to browser!)// 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();
Run through this checklist before every production deployment:
CSRF_SECRET generated and configured (32+ bytes)SESSION_SECRET generated and configured (32+ bytes)pk_live_..., sk_live_...).env.local NOT committed to git (check with git status)npm audit --production - 0 vulnerabilitiesnpm outdated - Check for critical security updatespackage-lock.json committed to gitsecurity-testing skill)node scripts/test-rate-limit.js)curl -I https://yourapp.com)node scripts/test-rate-limit.jscurl -I https://yourapp.com# 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
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
Authentication Metrics:
Rate Limiting Metrics:
CSRF Protection Metrics:
Input Validation Metrics:
Error Rate Metrics:
# 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
Monitor in Clerk dashboard:
// 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
});
}
High-Priority Alerts (Immediate Response):
Medium-Priority (24-hour Response):
Low-Priority (Weekly Review):
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
});
}
Implementation Guides:
.claude/skills/security/security-overview/SKILL.md - Overall architecture.claude/skills/security/*/SKILL.md - Individual security featuresdocs/security/SECURITY_IMPLEMENTATION.md - Complete implementation guideREADME.md - Security Configuration sectionAwareness & Learning:
.claude/skills/security/security-awareness/ - AI code vulnerability analysis.claude/skills/security/security-awareness/awareness-overview/ - Complete security overviewSecurity Testing:
scripts/test-rate-limit.js - Rate limiting verificationscripts/security-check.sh - Dependency auditscripts/security-test.sh - Comprehensive security test suite (if created)Example Implementations:
app/api/example-protected/route.ts - Complete security stack exampleapp/api/test-rate-limit/route.ts - Rate limiting test endpointapp/api/csrf/route.ts - CSRF token generationOWASP (Security Standards):
Framework & Service Docs:
Testing Tools:
npm audit --productionbash scripts/security-check.shnpm update + test# 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
🔒 Before Deployment:
🔒 After Deployment:
🔒 Continuous:
For implementation details, refer to individual security skills. For vulnerability awareness, refer to security-awareness skills.