Skip to content

Firebase Security Logging and Audit Trail

Name: Firebase Security Logging and Audit Trail

Applicable Services: All Firebase Services

Description

Security Monitoring Foundation

The Monitoring Gap: Many Firebase applications lack comprehensive security logging, making it difficult to detect attacks, perform incident response, or meet compliance requirements.

Intended Behavior: Security logging should capture all relevant events including authentication attempts, data access patterns, privilege changes, and security violations.

The Risk: Without proper logging, security incidents may go undetected, forensic analysis becomes impossible, and compliance obligations cannot be met.

Impact

Detection and Compliance Consequences

Inadequate security logging creates several critical gaps:

  • Delayed Threat Detection: Attacks may persist undetected for extended periods
  • Limited Incident Response: Insufficient data for forensic analysis and response
  • Compliance Violations: Failure to meet regulatory audit trail requirements
  • Insider Threat Blindness: Inability to detect malicious insider activities
  • Attack Attribution: Difficulty determining attack sources and methods

Implementation

Comprehensive Security Event Logging

Authentication Event Logging

// Cloud Function to log authentication events
exports.logAuthenticationEvents = functions.auth.user().onCreate(async (user) => {
  await logSecurityEvent({
    eventType: 'USER_CREATED',
    userId: user.uid,
    email: user.email,
    timestamp: new Date(),
    metadata: {
      provider: user.providerData[0]?.providerId,
      emailVerified: user.emailVerified,
      creationTime: user.metadata.creationTime
    }
  });
});

exports.logSignInEvents = functions.auth.user().onSignIn(async (user, context) => {
  await logSecurityEvent({
    eventType: 'USER_SIGN_IN',
    userId: user.uid,
    email: user.email,
    timestamp: new Date(),
    metadata: {
      provider: context.credential?.providerId,
      ipAddress: context.ipAddress,
      userAgent: context.userAgent,
      lastSignInTime: user.metadata.lastSignInTime
    }
  });
});

Database Access Logging

// Monitor Firestore security rule violations
exports.logFirestoreAccess = functions.firestore
  .document('{collection}/{docId}')
  .onWrite(async (change, context) => {
    const { collection, docId } = context.params;

    // Log all write operations to sensitive collections
    const sensitiveCollections = ['users', 'payments', 'admin_settings'];

    if (sensitiveCollections.includes(collection)) {
      await logSecurityEvent({
        eventType: 'SENSITIVE_DATA_ACCESS',
        collection: collection,
        documentId: docId,
        operation: change.before.exists ? 'UPDATE' : 'CREATE',
        userId: context.auth?.uid,
        timestamp: new Date(),
        metadata: {
          before: change.before.exists ? change.before.data() : null,
          after: change.after.exists ? change.after.data() : null,
          ipAddress: context.ipAddress
        }
      });
    }
  });

// Monitor for bulk operations that might indicate data exfiltration
exports.monitorBulkAccess = functions.firestore
  .document('access_patterns/{userId}')
  .onWrite(async (change, context) => {
    const data = change.after.data();

    if (data.readCount > 100) { // Threshold for bulk access
      await logSecurityEvent({
        eventType: 'BULK_DATA_ACCESS_DETECTED',
        userId: context.params.userId,
        readCount: data.readCount,
        timeWindow: data.timeWindow,
        severity: 'HIGH',
        timestamp: new Date()
      });
    }
  });

Function Execution Logging

// Wrapper for security-sensitive functions
function secureFunction(functionName, handler) {
  return functions.https.onCall(async (data, context) => {
    const startTime = Date.now();

    try {
      // Log function invocation
      await logSecurityEvent({
        eventType: 'FUNCTION_INVOCATION',
        functionName: functionName,
        userId: context.auth?.uid,
        timestamp: new Date(),
        metadata: {
          requestData: sanitizeRequestData(data),
          ipAddress: context.ipAddress,
          userAgent: context.userAgent
        }
      });

      // Execute the actual function
      const result = await handler(data, context);

      // Log successful execution
      await logSecurityEvent({
        eventType: 'FUNCTION_SUCCESS',
        functionName: functionName,
        userId: context.auth?.uid,
        executionTime: Date.now() - startTime,
        timestamp: new Date()
      });

      return result;

    } catch (error) {
      // Log function errors
      await logSecurityEvent({
        eventType: 'FUNCTION_ERROR',
        functionName: functionName,
        userId: context.auth?.uid,
        error: error.message,
        executionTime: Date.now() - startTime,
        severity: 'ERROR',
        timestamp: new Date()
      });

      throw error;
    }
  });
}

// Usage example
exports.transferFunds = secureFunction('transferFunds', async (data, context) => {
  // Secure function implementation
  return await processTransfer(data, context);
});

Centralized Security Event Storage

// Centralized security event logging function
async function logSecurityEvent(event) {
  try {
    // Add standard fields to all events
    const enrichedEvent = {
      ...event,
      id: generateEventId(),
      timestamp: event.timestamp || new Date(),
      source: 'firebase',
      version: '1.0'
    };

    // Store in Firestore for real-time monitoring
    await admin.firestore()
      .collection('security_events')
      .doc(enrichedEvent.id)
      .set(enrichedEvent);

    // Send to external SIEM if configured
    if (process.env.SIEM_WEBHOOK_URL) {
      await sendToSIEM(enrichedEvent);
    }

    // Trigger real-time alerts for high-severity events
    if (event.severity === 'HIGH' || event.severity === 'CRITICAL') {
      await triggerSecurityAlert(enrichedEvent);
    }

  } catch (error) {
    console.error('Failed to log security event:', error);

    // Fallback logging to ensure we don't lose critical events
    console.error('SECURITY_EVENT_FALLBACK:', JSON.stringify(event));
  }
}

function generateEventId() {
  return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
}

function sanitizeRequestData(data) {
  // Remove sensitive fields from request data
  const sensitiveFields = ['password', 'token', 'key', 'secret'];
  const sanitized = { ...data };

  for (const field of sensitiveFields) {
    if (field in sanitized) {
      sanitized[field] = '[REDACTED]';
    }
  }

  return sanitized;
}

Real-time Security Monitoring

// Real-time security event analysis
exports.analyzeSecurityEvents = functions.firestore
  .document('security_events/{eventId}')
  .onCreate(async (snap, context) => {
    const event = snap.data();

    // Analyze event for threats
    const threats = await analyzeEventForThreats(event);

    for (const threat of threats) {
      await handleSecurityThreat(threat, event);
    }
  });

async function analyzeEventForThreats(event) {
  const threats = [];

  // Check for brute force attacks
  if (event.eventType === 'USER_SIGN_IN' && event.metadata?.failed) {
    const recentFailures = await countRecentFailures(event.email, 15); // 15 minutes
    if (recentFailures > 5) {
      threats.push({
        type: 'BRUTE_FORCE_ATTACK',
        severity: 'HIGH',
        target: event.email,
        count: recentFailures
      });
    }
  }

  // Check for data exfiltration patterns
  if (event.eventType === 'BULK_DATA_ACCESS_DETECTED') {
    threats.push({
      type: 'POTENTIAL_DATA_EXFILTRATION',
      severity: 'CRITICAL',
      userId: event.userId,
      accessCount: event.readCount
    });
  }

  // Check for privilege escalation
  if (event.eventType === 'SENSITIVE_DATA_ACCESS' && 
      event.collection === 'admin_settings') {
    const userRole = await getUserRole(event.userId);
    if (userRole !== 'admin') {
      threats.push({
        type: 'UNAUTHORIZED_ADMIN_ACCESS',
        severity: 'CRITICAL',
        userId: event.userId,
        resource: event.collection
      });
    }
  }

  return threats;
}

async function handleSecurityThreat(threat, originalEvent) {
  // Log the threat detection
  await logSecurityEvent({
    eventType: 'THREAT_DETECTED',
    threatType: threat.type,
    severity: threat.severity,
    originalEventId: originalEvent.id,
    timestamp: new Date(),
    metadata: threat
  });

  // Take automated response actions
  switch (threat.type) {
    case 'BRUTE_FORCE_ATTACK':
      await temporarilyBlockIP(originalEvent.metadata?.ipAddress);
      break;

    case 'POTENTIAL_DATA_EXFILTRATION':
      await suspendUserAccount(threat.userId);
      break;

    case 'UNAUTHORIZED_ADMIN_ACCESS':
      await revokeUserSessions(threat.userId);
      break;
  }

  // Send alerts to security team
  await sendSecurityAlert(threat, originalEvent);
}

Integration with External Tools

// SIEM integration
async function sendToSIEM(event) {
  const siemPayload = {
    timestamp: event.timestamp.toISOString(),
    source: 'firebase',
    event_type: event.eventType,
    user_id: event.userId,
    severity: event.severity || 'INFO',
    message: formatEventMessage(event),
    raw_event: event
  };

  try {
    await fetch(process.env.SIEM_WEBHOOK_URL, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${process.env.SIEM_API_TOKEN}`
      },
      body: JSON.stringify(siemPayload)
    });
  } catch (error) {
    console.error('Failed to send event to SIEM:', error);
  }
}

// Slack integration for security alerts
async function sendSecurityAlert(threat, event) {
  const slackMessage = {
    text: `🚨 Security Threat Detected: ${threat.type}`,
    attachments: [{
      color: threat.severity === 'CRITICAL' ? 'danger' : 'warning',
      fields: [
        { title: 'Threat Type', value: threat.type, short: true },
        { title: 'Severity', value: threat.severity, short: true },
        { title: 'User ID', value: event.userId || 'Unknown', short: true },
        { title: 'Timestamp', value: event.timestamp.toISOString(), short: true }
      ]
    }]
  };

  try {
    await fetch(process.env.SLACK_WEBHOOK_URL, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(slackMessage)
    });
  } catch (error) {
    console.error('Failed to send Slack alert:', error);
  }
}

Best Practices

Log Management

  1. Structured Logging: Use consistent JSON format for all security events
  2. Event Enrichment: Add contextual information to all events
  3. Retention Policies: Establish appropriate log retention periods
  4. Data Privacy: Sanitize sensitive data in logs

Monitoring Strategy

  1. Real-time Analysis: Process security events as they occur
  2. Historical Analysis: Maintain searchable historical logs
  3. Correlation Rules: Identify complex attack patterns
  4. Alert Tuning: Minimize false positives while maintaining coverage

Integration Requirements

  1. SIEM Integration: Forward events to Security Information and Event Management systems
  2. Alerting Platforms: Integrate with incident response tools
  3. Compliance Reporting: Generate reports for regulatory requirements
  4. Backup and Recovery: Ensure log data is protected and recoverable

References