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¶
- Structured Logging: Use consistent JSON format for all security events
- Event Enrichment: Add contextual information to all events
- Retention Policies: Establish appropriate log retention periods
- Data Privacy: Sanitize sensitive data in logs
Monitoring Strategy¶
- Real-time Analysis: Process security events as they occur
- Historical Analysis: Maintain searchable historical logs
- Correlation Rules: Identify complex attack patterns
- Alert Tuning: Minimize false positives while maintaining coverage
Integration Requirements¶
- SIEM Integration: Forward events to Security Information and Event Management systems
- Alerting Platforms: Integrate with incident response tools
- Compliance Reporting: Generate reports for regulatory requirements
- Backup and Recovery: Ensure log data is protected and recoverable