Best Practices

Comprehensive security best practices, guidelines, and implementation strategies for secure application development.

Security Best Practices

This guide provides comprehensive security best practices for developing, deploying, and maintaining secure applications. Following these guidelines helps ensure robust security posture and protection against common threats.

Secure Development Lifecycle (SDL)

Security by Design

// lib/security/secure-design.ts
export class SecureDesign {
  
  static async implementSecurityByDesign() {
    return {
      threat_modeling: await this.performThreatModeling(),
      security_requirements: await this.defineSecurityRequirements(),
      secure_architecture: await this.reviewSecureArchitecture(),
      security_controls: await this.implementSecurityControls()
    }
  }
  
  private static async performThreatModeling() {
    const threats = [
      {
        id: 'T001',
        name: 'SQL Injection',
        category: 'Input Validation',
        severity: 'High',
        mitigation: 'Parameterized queries, input validation',
        status: 'Mitigated'
      },
      {
        id: 'T002',
        name: 'Cross-Site Scripting (XSS)',
        category: 'Input Validation',
        severity: 'High',
        mitigation: 'Content Security Policy, output encoding',
        status: 'Mitigated'
      },
      {
        id: 'T003',
        name: 'Authentication Bypass',
        category: 'Authentication',
        severity: 'Critical',
        mitigation: 'Multi-factor authentication, session management',
        status: 'Mitigated'
      }
    ]
    
    return {
      total_threats: threats.length,
      critical_threats: threats.filter(t => t.severity === 'Critical').length,
      mitigated_threats: threats.filter(t => t.status === 'Mitigated').length,
      threats: threats
    }
  }
  
  static getSecurityChecklist() {
    return {
      design_phase: [
        'Conduct threat modeling',
        'Define security requirements',
        'Design secure architecture',
        'Plan security controls',
        'Review data flow diagrams'
      ],
      development_phase: [
        'Use secure coding practices',
        'Implement input validation',
        'Apply output encoding',
        'Use parameterized queries',
        'Implement proper error handling'
      ],
      testing_phase: [
        'Perform security testing',
        'Conduct code reviews',
        'Run vulnerability scans',
        'Test security controls',
        'Validate threat mitigations'
      ],
      deployment_phase: [
        'Configure security settings',
        'Enable security monitoring',
        'Set up logging',
        'Configure firewalls',
        'Implement backup procedures'
      ]
    }
  }
}

Code Security Guidelines

// lib/security/coding-standards.ts
export class SecureCodingStandards {
  
  // Input Validation Best Practices
  static validateInput(input: string, type: 'email' | 'phone' | 'text' | 'number'): boolean {
    const patterns = {
      email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
      phone: /^\+?[\d\s\-\(\)]{10,}$/,
      text: /^[a-zA-Z0-9\s\-_.,!?]{1,1000}$/,
      number: /^\d+(\.\d+)?$/
    }
    
    // Always validate against whitelist
    if (!patterns[type].test(input)) {
      throw new Error(`Invalid ${type} format`)
    }
    
    // Check for common attack patterns
    const dangerousPatterns = [
      /<script/i,
      /javascript:/i,
      /vbscript:/i,
      /onload=/i,
      /onerror=/i,
      /\bUNION\b/i,
      /\bSELECT\b/i,
      /\bINSERT\b/i,
      /\bDELETE\b/i,
      /\bDROP\b/i
    ]
    
    for (const pattern of dangerousPatterns) {
      if (pattern.test(input)) {
        throw new Error('Input contains potentially dangerous content')
      }
    }
    
    return true
  }
  
  // Safe Database Queries
  static async safeQuery(query: string, params: any[]) {
    // Always use parameterized queries
    try {
      return await supabase.rpc('safe_query', {
        query_text: query,
        query_params: params
      })
    } catch (error) {
      // Never expose database errors to users
      console.error('Database error:', error)
      throw new Error('An error occurred while processing your request')
    }
  }
  
  // Secure Password Handling
  static async hashPassword(password: string): Promise<string> {
    const bcrypt = require('bcrypt')
    
    // Validate password strength
    if (!this.isPasswordStrong(password)) {
      throw new Error('Password does not meet security requirements')
    }
    
    // Use high cost factor for bcrypt
    const saltRounds = 12
    return await bcrypt.hash(password, saltRounds)
  }
  
  private static isPasswordStrong(password: string): boolean {
    const requirements = [
      password.length >= 12, // Minimum length
      /[A-Z]/.test(password), // Uppercase letter
      /[a-z]/.test(password), // Lowercase letter
      /\d/.test(password), // Number
      /[!@#$%^&*(),.?":{}|<>]/.test(password), // Special character
      !/(.)\1{2,}/.test(password) // No more than 2 consecutive identical characters
    ]
    
    return requirements.every(req => req === true)
  }
  
  // Secure Session Management
  static generateSecureSessionId(): string {
    const crypto = require('crypto')
    return crypto.randomBytes(32).toString('hex')
  }
  
  static async validateSession(sessionId: string): Promise<boolean> {
    if (!sessionId || typeof sessionId !== 'string') {
      return false
    }
    
    // Check session format
    if (!/^[a-f0-9]{64}$/.test(sessionId)) {
      return false
    }
    
    // Verify session exists and is valid
    const { data: session } = await supabase
      .from('user_sessions')
      .select('*')
      .eq('id', sessionId)
      .gte('expires_at', new Date().toISOString())
      .single()
    
    return !!session
  }
  
  // Secure Error Handling
  static handleError(error: Error, context: string): void {
    // Log detailed error for debugging
    console.error(`Error in ${context}:`, {
      message: error.message,
      stack: error.stack,
      timestamp: new Date().toISOString()
    })
    
    // Never expose internal errors to users
    const sanitizedMessage = this.getSanitizedErrorMessage(error.message)
    throw new Error(sanitizedMessage)
  }
  
  private static getSanitizedErrorMessage(originalMessage: string): string {
    // Map internal errors to user-friendly messages
    const errorMappings = {
      'connection refused': 'Service temporarily unavailable',
      'timeout': 'Request timed out, please try again',
      'unauthorized': 'Access denied',
      'forbidden': 'Insufficient permissions',
      'not found': 'Resource not found'
    }
    
    for (const [internal, external] of Object.entries(errorMappings)) {
      if (originalMessage.toLowerCase().includes(internal)) {
        return external
      }
    }
    
    return 'An unexpected error occurred'
  }
}

Infrastructure Security

Server Hardening

# server-hardening.sh
#!/bin/bash

# Update system packages
apt update && apt upgrade -y

# Configure firewall (UFW)
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable

# Disable root login
sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config

# Configure SSH key-only authentication
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart ssh

# Install and configure fail2ban
apt install fail2ban -y
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

# Configure automatic security updates
apt install unattended-upgrades -y
dpkg-reconfigure -plow unattended-upgrades

# Set up log monitoring
apt install logwatch -y
echo "Range = yesterday" >> /etc/logwatch/conf/logwatch.conf
echo "Detail = high" >> /etc/logwatch/conf/logwatch.conf

# Configure system limits
echo "* hard nofile 65536" >> /etc/security/limits.conf
echo "* soft nofile 65536" >> /etc/security/limits.conf

# Enable audit logging
apt install auditd -y
systemctl enable auditd
systemctl start auditd

Container Security

# Dockerfile.secure
FROM node:18-alpine AS base

# Create non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

# Set up app directory
WORKDIR /app
RUN chown nextjs:nodejs /app

# Copy package files
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force

# Copy application code
COPY --chown=nextjs:nodejs . .

# Remove unnecessary packages and files
RUN apk del --purge \
    && rm -rf /var/cache/apk/* \
    && rm -rf /tmp/* \
    && rm -rf /root/.npm

# Switch to non-root user
USER nextjs

# Expose port (non-privileged)
EXPOSE 3000

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3000/api/health || exit 1

CMD ["npm", "start"]

Kubernetes Security

# k8s-security-config.yaml
apiVersion: v1
kind: SecurityContext
metadata:
  name: app-security-context
spec:
  runAsNonRoot: true
  runAsUser: 1001
  runAsGroup: 1001
  readOnlyRootFilesystem: true
  allowPrivilegeEscalation: false
  capabilities:
    drop:
      - ALL

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: app-network-policy
spec:
  podSelector:
    matchLabels:
      app: secure-app
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 3000
  egress:
  - to:
    - podSelector:
        matchLabels:
          role: database
    ports:
    - protocol: TCP
      port: 5432

---
apiVersion: v1
kind: Pod
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1001
    fsGroup: 1001
  containers:
  - name: app
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
        - ALL
    resources:
      limits:
        memory: "512Mi"
        cpu: "500m"
      requests:
        memory: "256Mi"
        cpu: "250m"

Application Security

Security Headers Implementation

// middleware/security-headers.ts
export function securityHeaders() {
  return {
    // Prevent XSS attacks
    'X-XSS-Protection': '1; mode=block',
    
    // Prevent MIME type sniffing
    'X-Content-Type-Options': 'nosniff',
    
    // Control framing to prevent clickjacking
    'X-Frame-Options': 'DENY',
    
    // Enforce HTTPS
    'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload',
    
    // Control referrer information
    'Referrer-Policy': 'strict-origin-when-cross-origin',
    
    // Permissions policy
    'Permissions-Policy': 'camera=(), microphone=(), geolocation=()',
    
    // Content Security Policy
    'Content-Security-Policy': [
      "default-src 'self'",
      "script-src 'self' 'unsafe-eval' 'unsafe-inline'",
      "style-src 'self' 'unsafe-inline'",
      "img-src 'self' data: https:",
      "font-src 'self'",
      "connect-src 'self' https://api.supabase.co",
      "frame-ancestors 'none'",
      "form-action 'self'",
      "base-uri 'self'"
    ].join('; ')
  }
}

// Next.js middleware implementation
export function middleware(request: NextRequest) {
  const response = NextResponse.next()
  
  // Apply security headers
  const headers = securityHeaders()
  Object.entries(headers).forEach(([key, value]) => {
    response.headers.set(key, value)
  })
  
  return response
}

API Security Best Practices

// lib/api/security.ts
export class APISession {
  
  // Rate limiting implementation
  static async rateLimitCheck(
    identifier: string, 
    limit: number, 
    windowMs: number
  ): Promise<boolean> {
    const now = Date.now()
    const windowStart = now - windowMs
    
    // Clean up old entries
    await supabase
      .from('rate_limits')
      .delete()
      .lt('timestamp', new Date(windowStart).toISOString())
    
    // Count recent requests
    const { count } = await supabase
      .from('rate_limits')
      .select('*', { count: 'exact', head: true })
      .eq('identifier', identifier)
      .gte('timestamp', new Date(windowStart).toISOString())
    
    if ((count || 0) >= limit) {
      return false // Rate limit exceeded
    }
    
    // Record this request
    await supabase
      .from('rate_limits')
      .insert({
        identifier,
        timestamp: new Date().toISOString()
      })
    
    return true
  }
  
  // API key validation
  static async validateAPIKey(apiKey: string): Promise<boolean> {
    if (!apiKey || typeof apiKey !== 'string') {
      return false
    }
    
    // Check API key format
    if (!/^sk_[a-zA-Z0-9]{32}$/.test(apiKey)) {
      return false
    }
    
    // Verify API key exists and is active
    const { data: key } = await supabase
      .from('api_keys')
      .select('*')
      .eq('key_hash', await this.hashAPIKey(apiKey))
      .eq('active', true)
      .single()
    
    return !!key
  }
  
  private static async hashAPIKey(apiKey: string): Promise<string> {
    const crypto = require('crypto')
    return crypto.createHash('sha256').update(apiKey).digest('hex')
  }
  
  // Request validation
  static validateRequest(req: any): void {
    // Check content type for POST/PUT requests
    if (['POST', 'PUT', 'PATCH'].includes(req.method)) {
      const contentType = req.headers['content-type']
      if (!contentType || !contentType.includes('application/json')) {
        throw new Error('Invalid content type')
      }
    }
    
    // Validate request size
    const contentLength = parseInt(req.headers['content-length'] || '0')
    if (contentLength > 10 * 1024 * 1024) { // 10MB limit
      throw new Error('Request too large')
    }
    
    // Check for required headers
    const requiredHeaders = ['user-agent', 'authorization']
    for (const header of requiredHeaders) {
      if (!req.headers[header]) {
        throw new Error(`Missing required header: ${header}`)
      }
    }
  }
  
  // Response sanitization
  static sanitizeResponse(data: any): any {
    if (typeof data !== 'object' || data === null) {
      return data
    }
    
    const sensitiveFields = [
      'password',
      'token',
      'secret',
      'key',
      'hash',
      'salt',
      'private'
    ]
    
    const sanitized = { ...data }
    
    for (const field of sensitiveFields) {
      if (field in sanitized) {
        delete sanitized[field]
      }
    }
    
    return sanitized
  }
}

Security Testing

Automated Security Testing

// tests/security/security.test.ts
describe('Security Tests', () => {
  
  describe('Input Validation', () => {
    test('should reject malicious SQL injection attempts', async () => {
      const maliciousInputs = [
        "'; DROP TABLE users; --",
        "1' OR '1'='1",
        "admin'/*",
        "UNION SELECT * FROM users"
      ]
      
      for (const input of maliciousInputs) {
        await expect(async () => {
          await validateInput(input, 'text')
        }).rejects.toThrow()
      }
    })
    
    test('should reject XSS attempts', async () => {
      const xssPayloads = [
        '<script>alert("xss")</script>',
        'javascript:alert(1)',
        '<img src=x onerror=alert(1)>',
        '<svg onload=alert(1)>'
      ]
      
      for (const payload of xssPayloads) {
        await expect(async () => {
          await validateInput(payload, 'text')
        }).rejects.toThrow()
      }
    })
  })
  
  describe('Authentication', () => {
    test('should require strong passwords', async () => {
      const weakPasswords = [
        '123456',
        'password',
        'qwerty',
        'admin123',
        'password123'
      ]
      
      for (const password of weakPasswords) {
        await expect(async () => {
          await hashPassword(password)
        }).rejects.toThrow('Password does not meet security requirements')
      }
    })
    
    test('should validate session tokens', async () => {
      const invalidTokens = [
        '',
        'invalid',
        '123',
        'not-a-valid-session-id'
      ]
      
      for (const token of invalidTokens) {
        const isValid = await validateSession(token)
        expect(isValid).toBe(false)
      }
    })
  })
  
  describe('Rate Limiting', () => {
    test('should enforce rate limits', async () => {
      const identifier = 'test-user'
      const limit = 5
      const windowMs = 60000 // 1 minute
      
      // Make requests up to the limit
      for (let i = 0; i < limit; i++) {
        const allowed = await rateLimitCheck(identifier, limit, windowMs)
        expect(allowed).toBe(true)
      }
      
      // Next request should be blocked
      const blocked = await rateLimitCheck(identifier, limit, windowMs)
      expect(blocked).toBe(false)
    })
  })
})

// Security scanning integration
describe('Vulnerability Scanning', () => {
  test('should run OWASP dependency check', async () => {
    const { exec } = require('child_process')
    const { promisify } = require('util')
    const execAsync = promisify(exec)
    
    try {
      const { stdout, stderr } = await execAsync('npm audit --json')
      const auditResults = JSON.parse(stdout)
      
      // Check for high/critical vulnerabilities
      expect(auditResults.metadata.vulnerabilities.high).toBe(0)
      expect(auditResults.metadata.vulnerabilities.critical).toBe(0)
    } catch (error) {
      console.error('Security audit failed:', error)
      throw error
    }
  })
})

Penetration Testing Checklist

// Security testing checklist
export const securityTestingChecklist = {
  authentication: [
    'Test password complexity requirements',
    'Verify account lockout mechanisms',
    'Test multi-factor authentication',
    'Check session management',
    'Verify logout functionality',
    'Test password reset process'
  ],
  
  authorization: [
    'Test role-based access control',
    'Verify privilege escalation prevention',
    'Check resource-level permissions',
    'Test API endpoint authorization',
    'Verify admin panel security'
  ],
  
  input_validation: [
    'Test SQL injection prevention',
    'Verify XSS protection',
    'Check file upload security',
    'Test command injection prevention',
    'Verify input sanitization'
  ],
  
  session_management: [
    'Test session timeout',
    'Verify session invalidation',
    'Check concurrent session limits',
    'Test session fixation prevention',
    'Verify secure session storage'
  ],
  
  data_protection: [
    'Test data encryption at rest',
    'Verify data encryption in transit',
    'Check sensitive data handling',
    'Test data masking',
    'Verify secure data deletion'
  ],
  
  error_handling: [
    'Test error message sanitization',
    'Verify stack trace prevention',
    'Check logging security',
    'Test exception handling',
    'Verify error monitoring'
  ],
  
  infrastructure: [
    'Test network security',
    'Verify server hardening',
    'Check firewall configuration',
    'Test SSL/TLS configuration',
    'Verify security headers'
  ]
}

Security Monitoring

Continuous Security Monitoring

// lib/security/monitoring.ts
export class SecurityMonitoring {
  
  static async initializeMonitoring() {
    // Set up real-time security monitoring
    setInterval(async () => {
      await this.checkSecurityMetrics()
    }, 60000) // Check every minute
    
    // Set up daily security reports
    setInterval(async () => {
      await this.generateDailyReport()
    }, 24 * 60 * 60 * 1000) // Daily
    
    // Set up weekly vulnerability scans
    setInterval(async () => {
      await this.runVulnerabilityScans()
    }, 7 * 24 * 60 * 60 * 1000) // Weekly
  }
  
  private static async checkSecurityMetrics() {
    const metrics = {
      failed_logins: await this.getFailedLoginCount(),
      suspicious_ips: await this.getSuspiciousIPs(),
      error_rate: await this.getErrorRate(),
      security_events: await this.getSecurityEventCount()
    }
    
    // Alert on anomalies
    if (metrics.failed_logins > 100) {
      await this.sendSecurityAlert('High number of failed logins detected', 'high')
    }
    
    if (metrics.error_rate > 0.05) { // 5% error rate
      await this.sendSecurityAlert('High error rate detected', 'medium')
    }
    
    return metrics
  }
  
  private static async runVulnerabilityScans() {
    try {
      // Run automated vulnerability scans
      const { exec } = require('child_process')
      const { promisify } = require('util')
      const execAsync = promisify(exec)
      
      // Run npm audit
      const npmAudit = await execAsync('npm audit --json')
      
      // Run OWASP ZAP if available
      // const zapScan = await execAsync('zap-baseline.py -t http://localhost:3000')
      
      // Process and report results
      await this.processVulnerabilityResults(npmAudit.stdout)
      
    } catch (error) {
      console.error('Vulnerability scan failed:', error)
    }
  }
}

This comprehensive set of security best practices provides a solid foundation for building and maintaining secure applications while following industry standards and guidelines.