HIGH identification failuresactixfirestore

Identification Failures in Actix with Firestore

Identification Failures in Actix with Firestore — how this specific combination creates or exposes the vulnerability

Identification failures occur when an API fails to properly establish or enforce the identity of a principal before acting on its behalf. In an Actix web service that uses Google Cloud Firestore as the backend data store, this typically manifests as insecure direct object references (IDOR) or broken access control tied to resource ownership. Actix routes often bind path or query parameters directly to Firestore document IDs and then perform reads or writes without confirming that the authenticated subject is authorized for that specific document.

For example, an endpoint like /users/{user_id}/profile might extract user_id from the path and use it to construct a Firestore document path such as users/{user_id}. If the service only validates that a user is authenticated (via JWT or session) but does not verify that the authenticated user’s ID matches user_id, an attacker can change the path parameter to access or modify another user’s profile. Because Firestore security rules are not a substitute for application-level ownership checks, this creates an identification failure: the application identifies the resource by ID but fails to identify the caller’s relationship to that resource.

This issue is compounded when Firestore queries use incomplete filters or when developer code assumes that a document retrieved by ID is the document the caller is allowed to touch. An attacker can supply any valid document ID (e.g., from enumeration or from information leakage elsewhere) and the Actix handler may proceed to read or write that document if Firestore permissions allow it and the application does not enforce per-request ownership or role checks. Common root causes include missing binding between the authenticated subject and the Firestore document reference, lack of server-side canonical identifiers for the actor, and treating Firestore document IDs as trustworthy input without cross-checking against the authenticated principal’s permissions.

In a black-box scan, middleBrick tests for these identification failures by submitting unauthenticated and authenticated requests with manipulated identifiers (e.g., changing user_id or document IDs in paths and query parameters) and checking whether access is erroneously allowed. It also inspects OpenAPI specs to see whether path or query parameters are documented as identifiers and maps runtime behavior to ensure that every ID-based operation includes a corresponding authorization check. Findings are reported with severity and guidance, aligned to frameworks such as OWASP API Top 10 (2023) A01:2023 — Broken Object Level Authorization.

Firestore-Specific Remediation in Actix — concrete code fixes

Remediation focuses on ensuring that every Firestore operation is preceded by an explicit check that the authenticated principal is authorized for the target resource. Avoid relying on Firestore security rules alone for authorization in the application layer; treat them as a last line of defense, not the primary gate.

Principle: Bind identity to document references

Always resolve the Firestore document path using the authenticated subject’s identity rather than trusting user-supplied identifiers. Use a canonical mapping from authenticated subject (e.g., user UID from a verified JWT) to Firestore document path.

use actix_web::{web, HttpResponse, Result};
use google_cloud_firestore::client::Client;
use google_cloud_firestore::document::Document;

async fn get_user_profile(
    path: web::Path<(String,)>, // (user_id_from_path)
    client: web::Data<Client>,
    // Assume get_authenticated_uid returns the verified UID from the request
    auth: web::ReqData<AuthState>,
) -> Result<HttpResponse> {
    let (user_id_from_path,) = path.into_inner();
    let auth_uid = auth.as_ref().map_err(|_| actix_web::error::ErrorUnauthorized("Unauthorized"))?;

    // Ensure the caller can only access their own document
    if user_id_from_path != auth_uid {
        return Ok(HttpResponse::Forbidden().body("Access denied"));
    }

    let doc_ref = client.doc(&format!("users/{}", user_id_from_path));
    let doc: Document = doc_ref.get().await.map_err(|e| actix_web::error::ErrorInternalServerError(e.to_string()))?;
    if !doc.exists() {
        return Ok(HttpResponse::NotFound().body("Profile not found"));
    }
    // Process and return profile data
    Ok(HttpResponse::Ok().json(doc.data()))
}

Use Firestore queries with owner filters

When listing or searching collections, always include the authenticated subject as a filter condition to avoid exposing data across tenants.

async fn list_user_posts(
    client: web::Data<Client>,
    auth: web::ReqData<AuthState>,
) -> Result<HttpResponse> {
    let auth_uid = auth.as_ref().map_err(|_| actix_web::error::ErrorUnauthorized("Unauthorized"))?;

    // Query only posts belonging to the authenticated user
    let posts = client
        .collection("posts")
        .filter("user_id", "==", auth_uid)
        .get()
        .await
        .map_err(|e| actix_web::error::ErrorInternalServerError(e.to_string()))?;

    let items: Vec<serde_json::Value> = posts.into_iter().filter_map(|doc| doc.map(|d| d.data()).ok()).collect();
    Ok(HttpResponse::Ok().json(items))
}

Avoid ID enumeration via predictable IDs

If Firestore document IDs are predictable or sequential, attackers can iterate through them. Use additional access checks or obfuscate identifiers where appropriate, and always validate ownership on read/write.

async fn get_shared_resource(
    path: web::Path<(String, String)>, // (owner_id, resource_id)
    client: web::Data<Client>,
    auth: web::ReqData<AuthState>,
) -> Result<HttpResponse> {
    let (owner_id, resource_id) = path.into_inner();
    let auth_uid = auth.as_ref().map_err(|_| actix_web::error::ErrorUnauthorized("Unauthorized"))?;

    // Ensure the authenticated user is the owner before allowing access
    if owner_id != auth_uid {
        return Ok(HttpResponse::Forbidden().body("Access denied"));
    }

    let doc_ref = client.doc(&format!("owners/{}/resources/{}", owner_id, resource_id));
    let doc: Document = doc_ref.get().await.map_err(|e| actix_web::error::ErrorInternalServerError(e.to_string()))?;
    if !doc.exists() {
        return Ok(HttpResponse::NotFound().body("Resource not found"));
    }
    Ok(HttpResponse::Ok().json(doc.data()))
}

These fixes ensure that every Firestore operation is gated by an explicit identity check that ties the authenticated principal to the requested resource, mitigating identification failures. Combine these application-level checks with Firestore security rules for defense in depth, and validate that rules do not inadvertently permit broader access than intended.

Frequently Asked Questions

Can Firestore security rules alone prevent identification failures in Actix?
No. Firestore security rules are a backend safeguard and should complement application-level authorization. Relying on rules alone leaves gaps when the application does not enforce identity-to-resource binding, allowing attackers to exploit misconfigured rules or bypass them via malformed requests.
How does middleBrick detect identification failures in APIs using Firestore?
middleBrick tests unauthenticated and authenticated requests with manipulated identifiers (e.g., swapped user IDs or document references) and checks whether access is improperly allowed. It also analyzes OpenAPI specs for proper ownership documentation and maps runtime behavior to ensure that each ID-based operation includes authorization checks aligned to frameworks such as OWASP API Top 10.