Audit & Monitoring

Security auditing, monitoring, and logging for comprehensive threat detection and compliance.

Audit & Monitoring

Comprehensive security auditing and monitoring are essential for detecting threats, maintaining compliance, and ensuring system integrity. This guide covers audit logging, monitoring systems, and alerting mechanisms.

Audit Logging

Audit Log Structure

// lib/audit/types.ts
export interface AuditLog {
  id: string
  timestamp: string
  user_id?: string
  session_id?: string
  action: string
  resource_type: string
  resource_id?: string
  ip_address: string
  user_agent: string
  outcome: 'success' | 'failure' | 'error'
  details: Record<string, any>
  severity: 'low' | 'medium' | 'high' | 'critical'
  category: 'authentication' | 'authorization' | 'data_access' | 'system' | 'security'
}

export interface SecurityEvent {
  id: string
  type: string
  timestamp: string
  user_id?: string
  ip_address: string
  severity: 'low' | 'medium' | 'high' | 'critical'
  status: 'active' | 'investigating' | 'resolved'
  details: Record<string, any>
  automated_response?: string
}

Audit Logging Implementation

// lib/audit/logger.ts
export class AuditLogger {
  
  static async createLog(logData: Partial<AuditLog>) {
    const auditLog: AuditLog = {
      id: crypto.randomUUID(),
      timestamp: new Date().toISOString(),
      outcome: 'success',
      severity: 'low',
      category: 'system',
      ...logData
    }
    
    // Store in database
    await supabase
      .from('audit_logs')
      .insert(auditLog)
    
    // Send to external logging service if configured
    if (process.env.EXTERNAL_LOGGING_ENABLED === 'true') {
      await this.sendToExternalLogger(auditLog)
    }
    
    // Check for security patterns
    await this.analyzeSecurityPatterns(auditLog)
    
    return auditLog
  }
  
  static async logAuthentication(userId: string, action: string, outcome: 'success' | 'failure', details: any) {
    return await this.createLog({
      user_id: userId,
      action,
      resource_type: 'authentication',
      outcome,
      details,
      category: 'authentication',
      severity: outcome === 'failure' ? 'medium' : 'low'
    })
  }
  
  static async logDataAccess(userId: string, resourceType: string, resourceId: string, action: string, details: any) {
    return await this.createLog({
      user_id: userId,
      action,
      resource_type: resourceType,
      resource_id: resourceId,
      details,
      category: 'data_access',
      severity: 'low'
    })
  }
  
  static async logSecurityEvent(eventType: string, severity: 'low' | 'medium' | 'high' | 'critical', details: any) {
    return await this.createLog({
      action: 'security_event',
      resource_type: 'security',
      details: { event_type: eventType, ...details },
      category: 'security',
      severity
    })
  }
  
  private static async sendToExternalLogger(auditLog: AuditLog) {
    try {
      await fetch(process.env.EXTERNAL_LOGGING_URL!, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${process.env.EXTERNAL_LOGGING_TOKEN}`
        },
        body: JSON.stringify(auditLog)
      })
    } catch (error) {
      console.error('Failed to send audit log to external service:', error)
    }
  }
  
  private static async analyzeSecurityPatterns(auditLog: AuditLog) {
    // Check for suspicious patterns
    if (auditLog.outcome === 'failure' && auditLog.category === 'authentication') {
      await this.checkFailedLoginPatterns(auditLog)
    }
    
    if (auditLog.category === 'data_access') {
      await this.checkDataAccessPatterns(auditLog)
    }
  }
  
  private static async checkFailedLoginPatterns(auditLog: AuditLog) {
    const recentFailures = await supabase
      .from('audit_logs')
      .select('*')
      .eq('ip_address', auditLog.ip_address)
      .eq('outcome', 'failure')
      .eq('category', 'authentication')
      .gte('timestamp', new Date(Date.now() - 15 * 60 * 1000).toISOString())
    
    if (recentFailures.data && recentFailures.data.length >= 5) {
      await SecurityMonitor.createAlert({
        type: 'brute_force_attempt',
        severity: 'high',
        details: {
          ip_address: auditLog.ip_address,
          failure_count: recentFailures.data.length,
          time_window: '15 minutes'
        }
      })
    }
  }
  
  private static async checkDataAccessPatterns(auditLog: AuditLog) {
    const recentAccess = await supabase
      .from('audit_logs')
      .select('*')
      .eq('user_id', auditLog.user_id)
      .eq('category', 'data_access')
      .gte('timestamp', new Date(Date.now() - 60 * 60 * 1000).toISOString())
    
    if (recentAccess.data && recentAccess.data.length > 100) {
      await SecurityMonitor.createAlert({
        type: 'excessive_data_access',
        severity: 'medium',
        details: {
          user_id: auditLog.user_id,
          access_count: recentAccess.data.length,
          time_window: '1 hour'
        }
      })
    }
  }
}

Security Monitoring

Real-time Monitoring System

// lib/monitoring/security.ts
export class SecurityMonitor {
  
  static async createAlert(alertData: {
    type: string
    severity: 'low' | 'medium' | 'high' | 'critical'
    details: Record<string, any>
    user_id?: string
    ip_address?: string
  }) {
    const alert: SecurityEvent = {
      id: crypto.randomUUID(),
      timestamp: new Date().toISOString(),
      status: 'active',
      ...alertData
    }
    
    // Store alert
    await supabase
      .from('security_events')
      .insert(alert)
    
    // Send notifications based on severity
    await this.handleAlertNotifications(alert)
    
    // Execute automated responses
    await this.executeAutomatedResponse(alert)
    
    return alert
  }
  
  static async monitorLoginAttempts() {
    const fifteenMinutesAgo = new Date(Date.now() - 15 * 60 * 1000).toISOString()
    
    const { data: failedAttempts } = await supabase
      .from('audit_logs')
      .select('ip_address, count(*)')
      .eq('outcome', 'failure')
      .eq('category', 'authentication')
      .gte('timestamp', fifteenMinutesAgo)
      .group('ip_address')
      .having('count(*) >= 5')
    
    for (const attempt of failedAttempts || []) {
      await this.createAlert({
        type: 'brute_force_detected',
        severity: 'high',
        ip_address: attempt.ip_address,
        details: {
          failure_count: attempt.count,
          time_window: '15 minutes'
        }
      })
    }
  }
  
  static async monitorDataAccess() {
    const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString()
    
    const { data: accessPatterns } = await supabase
      .from('audit_logs')
      .select('user_id, count(*)')
      .eq('category', 'data_access')
      .gte('timestamp', oneHourAgo)
      .group('user_id')
      .having('count(*) > 500')
    
    for (const pattern of accessPatterns || []) {
      await this.createAlert({
        type: 'anomalous_data_access',
        severity: 'medium',
        user_id: pattern.user_id,
        details: {
          access_count: pattern.count,
          time_window: '1 hour'
        }
      })
    }
  }
  
  static async monitorSystemHealth() {
    // Check error rates
    const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000).toISOString()
    
    const { data: errorRate } = await supabase
      .from('audit_logs')
      .select('count(*)')
      .eq('outcome', 'error')
      .gte('timestamp', fiveMinutesAgo)
      .single()
    
    if (errorRate && errorRate.count > 50) {
      await this.createAlert({
        type: 'high_error_rate',
        severity: 'high',
        details: {
          error_count: errorRate.count,
          time_window: '5 minutes'
        }
      })
    }
  }
  
  private static async handleAlertNotifications(alert: SecurityEvent) {
    // Critical alerts - immediate notification
    if (alert.severity === 'critical') {
      await this.sendImmediateNotification(alert)
    }
    
    // High severity - notify security team
    if (alert.severity === 'high') {
      await this.notifySecurityTeam(alert)
    }
    
    // Medium severity - email notification
    if (alert.severity === 'medium') {
      await this.sendEmailNotification(alert)
    }
  }
  
  private static async executeAutomatedResponse(alert: SecurityEvent) {
    switch (alert.type) {
      case 'brute_force_detected':
        await this.blockIP(alert.ip_address!, '1 hour')
        break
      
      case 'excessive_data_access':
        await this.suspendUser(alert.user_id!, 'security_review')
        break
      
      case 'sql_injection_attempt':
        await this.blockIP(alert.ip_address!, '24 hours')
        break
    }
  }
  
  private static async blockIP(ipAddress: string, duration: string) {
    await supabase
      .from('blocked_ips')
      .insert({
        ip_address: ipAddress,
        reason: 'automated_security_response',
        expires_at: this.calculateExpiryTime(duration),
        created_at: new Date().toISOString()
      })
    
    await AuditLogger.logSecurityEvent('ip_blocked', 'medium', {
      ip_address: ipAddress,
      duration,
      automated: true
    })
  }
  
  private static async suspendUser(userId: string, reason: string) {
    await supabase
      .from('users')
      .update({
        status: 'suspended',
        suspended_reason: reason,
        suspended_at: new Date().toISOString()
      })
      .eq('id', userId)
    
    await AuditLogger.logSecurityEvent('user_suspended', 'high', {
      user_id: userId,
      reason,
      automated: true
    })
  }
  
  private static calculateExpiryTime(duration: string): string {
    const now = new Date()
    
    if (duration === '1 hour') {
      return new Date(now.getTime() + 60 * 60 * 1000).toISOString()
    } else if (duration === '24 hours') {
      return new Date(now.getTime() + 24 * 60 * 60 * 1000).toISOString()
    }
    
    return new Date(now.getTime() + 60 * 60 * 1000).toISOString()
  }
}

Monitoring Dashboard

Security Metrics Collection

// lib/monitoring/metrics.ts
export class SecurityMetrics {
  
  static async getSecurityOverview() {
    const now = new Date()
    const twentyFourHoursAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000).toISOString()
    
    const [
      totalEvents,
      criticalAlerts,
      failedLogins,
      dataBreaches,
      blockedIPs
    ] = await Promise.all([
      this.getTotalSecurityEvents(twentyFourHoursAgo),
      this.getCriticalAlerts(twentyFourHoursAgo),
      this.getFailedLogins(twentyFourHoursAgo),
      this.getDataBreaches(twentyFourHoursAgo),
      this.getBlockedIPs()
    ])
    
    return {
      total_events: totalEvents,
      critical_alerts: criticalAlerts,
      failed_logins: failedLogins,
      data_breaches: dataBreaches,
      blocked_ips: blockedIPs,
      last_updated: now.toISOString()
    }
  }
  
  static async getThreatTrends() {
    const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString()
    
    const { data: trends } = await supabase
      .from('security_events')
      .select('type, severity, timestamp')
      .gte('timestamp', thirtyDaysAgo)
      .order('timestamp', { ascending: true })
    
    return this.processTrendData(trends || [])
  }
  
  static async getTopThreats() {
    const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString()
    
    const { data: threats } = await supabase
      .from('security_events')
      .select('type, count(*)')
      .gte('timestamp', sevenDaysAgo)
      .group('type')
      .order('count', { ascending: false })
      .limit(10)
    
    return threats || []
  }
  
  static async getGeographicThreats() {
    const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString()
    
    const { data: threats } = await supabase
      .from('security_events')
      .select('ip_address, type, severity, timestamp')
      .gte('timestamp', twentyFourHoursAgo)
      .not('ip_address', 'is', null)
    
    // Group by IP and enrich with geographic data
    return this.enrichWithGeoData(threats || [])
  }
  
  private static async getTotalSecurityEvents(since: string) {
    const { count } = await supabase
      .from('security_events')
      .select('*', { count: 'exact', head: true })
      .gte('timestamp', since)
    
    return count || 0
  }
  
  private static async getCriticalAlerts(since: string) {
    const { count } = await supabase
      .from('security_events')
      .select('*', { count: 'exact', head: true })
      .eq('severity', 'critical')
      .gte('timestamp', since)
    
    return count || 0
  }
  
  private static async getFailedLogins(since: string) {
    const { count } = await supabase
      .from('audit_logs')
      .select('*', { count: 'exact', head: true })
      .eq('category', 'authentication')
      .eq('outcome', 'failure')
      .gte('timestamp', since)
    
    return count || 0
  }
  
  private static async getDataBreaches(since: string) {
    const { count } = await supabase
      .from('security_events')
      .select('*', { count: 'exact', head: true })
      .eq('type', 'data_breach')
      .gte('timestamp', since)
    
    return count || 0
  }
  
  private static async getBlockedIPs() {
    const { count } = await supabase
      .from('blocked_ips')
      .select('*', { count: 'exact', head: true })
      .gte('expires_at', new Date().toISOString())
    
    return count || 0
  }
}

Compliance Reporting

Audit Trail Generation

// lib/audit/reporting.ts
export class ComplianceReporter {
  
  static async generateAuditReport(startDate: string, endDate: string, format: 'json' | 'csv' | 'pdf' = 'json') {
    const auditData = await supabase
      .from('audit_logs')
      .select('*')
      .gte('timestamp', startDate)
      .lte('timestamp', endDate)
      .order('timestamp', { ascending: true })
    
    const report = {
      report_id: crypto.randomUUID(),
      generated_at: new Date().toISOString(),
      period: { start: startDate, end: endDate },
      total_events: auditData.data?.length || 0,
      events: auditData.data || [],
      summary: await this.generateSummary(auditData.data || [])
    }
    
    switch (format) {
      case 'csv':
        return this.convertToCSV(report)
      case 'pdf':
        return this.convertToPDF(report)
      default:
        return report
    }
  }
  
  static async generateComplianceReport(standard: 'SOC2' | 'ISO27001' | 'GDPR' | 'HIPAA') {
    const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString()
    
    const baseData = await supabase
      .from('audit_logs')
      .select('*')
      .gte('timestamp', thirtyDaysAgo)
    
    const securityEvents = await supabase
      .from('security_events')
      .select('*')
      .gte('timestamp', thirtyDaysAgo)
    
    switch (standard) {
      case 'SOC2':
        return this.generateSOC2Report(baseData.data || [], securityEvents.data || [])
      case 'ISO27001':
        return this.generateISO27001Report(baseData.data || [], securityEvents.data || [])
      case 'GDPR':
        return this.generateGDPRReport(baseData.data || [], securityEvents.data || [])
      case 'HIPAA':
        return this.generateHIPAAReport(baseData.data || [], securityEvents.data || [])
    }
  }
  
  private static async generateSummary(events: AuditLog[]) {
    const categories = events.reduce((acc, event) => {
      acc[event.category] = (acc[event.category] || 0) + 1
      return acc
    }, {} as Record<string, number>)
    
    const outcomes = events.reduce((acc, event) => {
      acc[event.outcome] = (acc[event.outcome] || 0) + 1
      return acc
    }, {} as Record<string, number>)
    
    const severities = events.reduce((acc, event) => {
      acc[event.severity] = (acc[event.severity] || 0) + 1
      return acc
    }, {} as Record<string, number>)
    
    return {
      by_category: categories,
      by_outcome: outcomes,
      by_severity: severities,
      unique_users: new Set(events.map(e => e.user_id).filter(Boolean)).size,
      unique_ips: new Set(events.map(e => e.ip_address)).size
    }
  }
  
  private static generateSOC2Report(auditData: AuditLog[], securityEvents: SecurityEvent[]) {
    return {
      standard: 'SOC2',
      trust_service_criteria: {
        security: {
          access_controls: this.analyzeAccessControls(auditData),
          logical_and_physical_controls: this.analyzeLogicalControls(auditData),
          system_operations: this.analyzeSystemOperations(auditData),
          change_management: this.analyzeChangeManagement(auditData),
          risk_mitigation: this.analyzeRiskMitigation(securityEvents)
        }
      },
      exceptions: this.identifySOC2Exceptions(auditData, securityEvents),
      recommendations: this.generateSOC2Recommendations(auditData, securityEvents)
    }
  }
}

Alerting System

Multi-channel Notifications

// lib/monitoring/alerts.ts
export class AlertManager {
  
  static async sendAlert(alert: SecurityEvent) {
    const channels = this.getAlertChannels(alert.severity)
    
    for (const channel of channels) {
      try {
        await this.sendToChannel(channel, alert)
      } catch (error) {
        console.error(`Failed to send alert to ${channel}:`, error)
      }
    }
  }
  
  private static getAlertChannels(severity: string): string[] {
    switch (severity) {
      case 'critical':
        return ['slack', 'email', 'sms', 'webhook']
      case 'high':
        return ['slack', 'email', 'webhook']
      case 'medium':
        return ['email', 'webhook']
      default:
        return ['webhook']
    }
  }
  
  private static async sendToChannel(channel: string, alert: SecurityEvent) {
    switch (channel) {
      case 'slack':
        await this.sendSlackAlert(alert)
        break
      case 'email':
        await this.sendEmailAlert(alert)
        break
      case 'sms':
        await this.sendSMSAlert(alert)
        break
      case 'webhook':
        await this.sendWebhookAlert(alert)
        break
    }
  }
  
  private static async sendSlackAlert(alert: SecurityEvent) {
    const webhook = process.env.SLACK_WEBHOOK_URL
    if (!webhook) return
    
    const message = {
      text: `🚨 Security Alert: ${alert.type}`,
      attachments: [{
        color: this.getSeverityColor(alert.severity),
        fields: [
          { title: 'Severity', value: alert.severity.toUpperCase(), short: true },
          { title: 'Time', value: alert.timestamp, short: true },
          { title: 'Details', value: JSON.stringify(alert.details, null, 2), short: false }
        ]
      }]
    }
    
    await fetch(webhook, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(message)
    })
  }
  
  private static getSeverityColor(severity: string): string {
    switch (severity) {
      case 'critical': return 'danger'
      case 'high': return 'warning'
      case 'medium': return 'good'
      default: return 'good'
    }
  }
}

This comprehensive audit and monitoring system provides real-time security monitoring, automated threat detection, compliance reporting, and multi-channel alerting to maintain system security and regulatory compliance.