Skip to content

Firestore: Misconfigured TTL in Documents

Name: Misconfigured TTL in Firestore Documents

Applicable Services: Cloud Firestore

Description

Data Retention and Compliance Risk

The Configuration Issue: Firestore documents that should be temporary lack proper Time-To-Live (TTL) configuration, causing them to persist indefinitely.

Intended Behavior: Temporary documents (sessions, tokens, cache data, temporary uploads) should have TTL policies that automatically delete them after a specified period.

The Risk: Missing TTL configuration leads to data bloat, increased storage costs, privacy violations, and potential compliance issues with data retention regulations.

Impact

Business and Compliance Consequences

Misconfigured TTL policies create several risks:

  • Compliance Violations: Violates GDPR, CCPA, and other data retention requirements
  • Privacy Risk: Sensitive temporary data persists longer than necessary
  • Storage Costs: Unnecessary storage charges for expired data
  • Performance Impact: Large collections slow down queries and operations
  • Security Risk: Stale authentication tokens and sessions remain valid
  • Data Governance: Difficulty maintaining clean data lifecycle management

Example Attack Scenarios

Exploit Scenario: Stale Session Token Abuse

The Vulnerable Pattern:

// VULNERABLE: Session tokens without TTL
await db.collection('user_sessions').doc(sessionId).set({
  userId: user.uid,
  token: sessionToken,
  createdAt: new Date(),
  // No TTL field - session persists forever
  permissions: ['read', 'write']
});

Attack Impact: - Old session tokens remain valid indefinitely - Compromised tokens can be used long after intended expiration - Increased attack surface from accumulated stale sessions

Mitigation

Configure TTL Policies

// VULNERABLE: No TTL configuration
await db.collection('temporary_uploads').doc(uploadId).set({
  fileUrl: tempFileUrl,
  uploadedAt: new Date(),
  userId: user.uid
  // Missing TTL - file metadata persists forever
});
// SECURE: Proper TTL configuration
await db.collection('temporary_uploads').doc(uploadId).set({
  fileUrl: tempFileUrl,
  uploadedAt: new Date(),
  userId: user.uid,
  // TTL field for automatic deletion after 24 hours
  ttl: new Date(Date.now() + 24 * 60 * 60 * 1000)
});

Automated Cleanup Functions

// Implement cleanup functions for collections without TTL
exports.cleanupExpiredSessions = functions.pubsub
  .schedule('every 24 hours')
  .onRun(async (context) => {
    const expiredThreshold = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); // 7 days

    const expiredSessions = await db.collection('user_sessions')
      .where('createdAt', '<', expiredThreshold)
      .get();

    const batch = db.batch();
    expiredSessions.docs.forEach(doc => {
      batch.delete(doc.ref);
    });

    await batch.commit();
    console.log(`Cleaned up ${expiredSessions.size} expired sessions`);
  });

References