Skip to content

Firebase Security Vulnerability: FBR-LOGIC-002

Name: Incorrect Usage of resource vs. request.resource

Applicable Services: Cloud Firestore

Description

Note

This logical vulnerability occurs when a security rule for an update operation confuses the resource variable (the document's current state) with the request.resource variable (the document's proposed future state).

  • The Technical Flaw: The rule fails to correctly compare the existing data with the incoming data. A common mistake is trying to enforce an immutable field by checking request.resource.data.field == request.resource.data.field, which is a tautology and provides no security.
  • Intended Behavior: For update operations, rules should use resource to access the document's data before the change and request.resource to access the data being sent by the client. This allows for powerful comparisons, suchs as ensuring a createdAt timestamp is not modified or that a user's role cannot be changed.
  • The Risk: This misuse completely nullifies any state-based validation. It can allow users to alter fields that should be immutable (like user roles or creation dates), bypass data validation checks, and escalate their privileges within the application.

Impact

Potential Consequences

The severity of this vulnerability is an error. Exploiting it can lead to: * Privilege Escalation: Users can modify immutable properties like role or isAdmin flags to grant themselves unauthorized permissions. * Data Corruption: Critical, immutable data (e.g., authorId, createdAt timestamps) can be altered, leading to data integrity loss and application errors. * Bypassed Validation: Rules that validate a state transition (e.g., an order status can only change from processing to shipped) can be circumvented.

Example Attack Scenarios

Exploit Scenario: Bypassing an Immutability Check to Gain Admin Rights

  • The Vulnerable Rule: The following Firestore rule attempts to make the role field on a user profile immutable. However, it incorrectly compares the incoming role to itself, rendering the check useless.
    // VULNERABLE: This rule incorrectly uses request.resource twice.
    match /users/{userId} {
      allow update: if request.auth.uid == userId &&
                       request.resource.data.role == request.resource.data.role; // This is always true!
    }
    
  • Attacker's Goal: To change their role from "user" to "admin".
  • The Exploit: The attacker, who is authenticated as a regular user, sends a client-side request to update their own user document, setting the role field to "admin".
    // Attacker with UID "user-123" is logged in.
    // Their current role is "user".
    db.collection("users").doc("user-123").update({
      role: "admin" // Attempting to escalate privileges
    })
    .then(() => console.log("Successfully escalated to admin!"))
    .catch((err) => console.error("Update failed: ", err));
    
  • Outcome: The security rule evaluates request.resource.data.role == request.resource.data.role as "admin" == "admin", which is true. The update is allowed, and the attacker successfully escalates their privileges to an administrator, bypassing the intended security control.

Mitigation

The vulnerability is resolved by correctly using resource.data to refer to the existing data and request.resource.data for the incoming data. This allows the rule to perform a meaningful comparison between the old and new states.

// VULNERABLE: Compares the incoming data to itself.
match /users/{userId} {
  allow update: if request.auth.uid == userId &&
                   request.resource.data.role == request.resource.data.role;
}
// SECURE: Correctly compares the incoming role to the existing role.
match /users/{userId} {
  allow update: if request.auth.uid == userId &&
                   request.resource.data.role == resource.data.role;
}

Best Practice

When writing update rules, always ask: "What was the data before this change?" (use resource) and "What is the client trying to change it to?" (use request.resource). Use this pattern for all fields that should be immutable or have constrained state transitions.

References