Skip to content

Firebase Security Vulnerability: FBR-PERF-002

Name: Lack of Rate Limiting on Write Operations

Applicable Services: Cloud Firestore, Firebase Realtime Database, Cloud Storage

Description

Note

This performance and cost vulnerability occurs when security rules fail to enforce a limit on how frequently a user can perform write operations.

  • The Technical Flaw: The security rule for a create or update operation only validates authentication or basic data, but it lacks a time-based condition to check how recently the user last performed a similar action.
  • Intended Behavior: Critical or expensive operations should be rate-limited. A user should only be able to perform a certain number of actions within a given time window to prevent abuse, whether intentional or accidental.
  • The Risk: An unconstrained write operation can be exploited to drive up operational costs significantly. An attacker can write a simple script to create thousands of documents per minute, with each create operation adding to your Firebase bill. This can also lead to performance degradation for other users.

Impact

Potential Consequences

The severity of this vulnerability is a warning. Exploiting it can lead to: * Excessive Costs: The primary impact is financial. Automated scripts can generate thousands or millions of write operations, leading to a surprisingly large bill. * Denial of Service (DoS): While less common, extremely high-frequency writes to a specific part of your database can create a "hotspot," degrading read and write performance for legitimate users and potentially making the application unresponsive. * Data Spam: The database can be flooded with useless or malicious data, making moderation difficult and degrading the user experience.

Example Attack Scenarios

Exploit Scenario: Spamming a Collection with Unrestricted Writes

  • The Vulnerable Rule: The following Firestore rule allows any authenticated user to create a document in the user_feedback collection. There is no limit on how often they can do this.
    // VULNERABLE: Any authenticated user can create documents as fast as they want.
    match /user_feedback/{feedbackId} {
      allow create: if request.auth != null;
    }
    
  • Attacker's Goal: To inflict financial damage by creating a massive number of documents.
  • The Exploit: The attacker writes a simple client-side script that loops indefinitely, creating a new feedback document in each iteration.
    // Attacker runs this script in a loop.
    // Each iteration is a billable write operation.
    for (let i = 0; i < 10000; i++) {
      db.collection("user_feedback").add({
        comment: "This is spam comment #" + i,
        timestamp: new Date()
      })
      .then(() => console.log("Spam document written."))
      .catch((err) => console.error("Write failed, but we will retry: ", err));
    }
    
  • Outcome: The script successfully creates 10,000 new documents in a very short time. The attacker has generated 10,000 billable write operations, and the user_feedback collection is now filled with spam.

Mitigation

The vulnerability is resolved by implementing a rate-limiting mechanism in the security rules. This is typically done by maintaining a separate document that tracks the timestamp of a user's last action. The rule then checks this timestamp before allowing a new operation.

First, create a separate collection (e.g., user_metadata) to store the last write time for each user.

// VULNERABLE: No rate limiting.
match /user_feedback/{feedbackId} {
  allow create: if request.auth != null;
}
// SECURE: Checks the user's last write time before allowing a new one.
match /user_feedback/{feedbackId} {
  allow create: if request.auth != null &&
                   // Get the user's metadata document.
                   let lastWrite = get(/databases/$(database)/documents/user_metadata/$(request.auth.uid)).data.lastFeedbackTime;
                   // Allow if the last write was more than 5 seconds ago.
                   request.time > lastWrite + duration.value(5, 's');
}

Best Practice

After the client successfully writes a user_feedback document, it must immediately update the lastFeedbackTime field in the /user_metadata/$(request.auth.uid) document to the current server timestamp. This "resets the clock" for the next operation. This can be done securely using a batched write on the client.

References