HIGH webhook abuseactixfirestore

Webhook Abuse in Actix with Firestore

Webhook Abuse in Actix with Firestore — how this specific combination creates or exposes the vulnerability

Webhook abuse in an Actix service that uses Firestore as a backend typically arises when incoming webhook endpoints accept untrusted data and use it to construct Firestore operations without strict validation or authorization checks. Because Actix does not enforce schema validation on webhook payloads by default, an attacker can send crafted JSON that maps to Firestore document paths, collection names, or field keys, leading to insecure direct object references (BOLA/IDOR) or over-privileged write operations.

In this combination, Firestore-specific risks include:

  • Path or ID manipulation: If the webhook extracts an identifier such as user_id or document_id from the payload and uses it directly in a Firestore path (e.g., users/{user_id}/requests/{doc_id}), an attacker can enumerate or target other users’ documents if access rules are not scoped per requestor.
  • Unconstrained writes: When Actix handlers write data from webhooks into Firestore without verifying the caller’s intent, an attacker can inject fields that change ownership, escalate privileges, or poison data used by downstream services.
  • Mass assignment or property manipulation: If Firestore documents contain administrative flags (e.g., is_admin, role), and the handler merges the entire JSON body into an existing document, a webhook request can elevate privileges by setting these fields.

These issues map to common findings in the BOLA/IDOR and Property Authorization checks run by middleBrick. For example, a scan may flag an endpoint that accepts a document_id from the body and uses it to read or write Firestore without confirming the requester’s ownership. Because the scan tests the unauthenticated attack surface, it can detect whether paths or fields are influenced by attacker-controlled data and whether the exposed surface allows enumeration or unauthorized modification.

To illustrate, consider an Actix webhook that updates a Firestore document based on incoming JSON:

async fn handle_webhook(
    webhook_data: web::Json,
    firestore: web::Data,
) -> impl Responder {
    let doc_id = webhook_data["document_id"].as_str().unwrap_or("unknown");
    let collection = webhook_data["collection"].as_str().unwrap_or("requests");
    // Unsafe: using attacker-controlled values directly in paths
    let doc_ref = firestore.collection(collection).doc(doc_id);
    doc_ref.set(webhook_data.0.clone()).await.map_err(|e| ...)
}

In this pattern, collection and document_id are taken directly from the webhook payload. A security scan would highlight this as BOLA/IDOR risk and property authorization risk because there is no check that the authenticated context (if present) is allowed to write to the specified collection or document. Even in unauthenticated webhook scenarios, such endpoints can be abused to flood Firestore with writes or read sensitive collections if the rules permit.

middleBrick’s LLM/AI Security checks are not relevant here, but its API security checks will flag the absence of input validation and authorization boundaries. The scanner will note that the endpoint accepts external input for resource identifiers and does not enforce a strict allowlist on collection names or document paths, which can lead to data exposure or unauthorized modification.

Firestore-Specific Remediation in Actix — concrete code fixes

Remediation centers on strict validation, scoping writes to the caller’s identity, and avoiding direct concatenation of untrusted values into Firestore paths. Below are concrete, safe patterns for Actix handlers that interact with Firestore.

1. Validate and restrict collection names

Use a hardcoded allowlist instead of reflecting the collection field from the webhook:

async fn handle_webhook_safe(
    webhook_data: web::Json,
    firestore: web::Data,
    caller_id: String, // derived from auth or request context
) -> impl Responder {
    // Only allow known, safe collections
    let allowed_collections = ["requests", "events"];
    let collection_name = webhook_data["collection"].as_str().filter(|&c| allowed_collections.contains(&c)).unwrap_or("requests");

    // Use caller_id to scope the document path
    let doc_id = webhook_data["local_id"].as_str().unwrap_or_else(|| {
        // Generate a server-side ID to avoid IDOR via ID manipulation
        uuid::Uuid::new_v4().to_string()
    });
    let doc_ref = firestore.collection(collection_name).doc(&format!("{}_{}", caller_id, doc_id));

    // Explicitly filter fields to prevent mass assignment
    let mut data = serde_json::Map::new();
    if let Some(payload) = webhook_data.get("payload") {
        data.insert("payload".to_string(), payload.clone());
    }
    data.insert("created_by".to_string(), Value::String(caller_id));

    doc_ref.set(data).await.map_err(|e| ...)
}

This approach ensures collection names are controlled, document IDs are scoped to the caller, and only intended fields are written, mitigating BOLA/IDOR and Property Authorization risks.

2. Use Firestore transactions for integrity

When updates must depend on existing data, use a transaction to avoid race conditions and ensure the caller is allowed to modify the target document:

async fn update_with_transaction(
    firestore: web::Data,
    caller_id: String,
    local_id: String,
) -> Result<(), Error> {
    let doc_path = format!("users/{}/requests/{}", caller_id, local_id);
    firestore.run_transaction(|transaction| async move {
        let snapshot = transaction.get(&doc_path).await?;
        if !snapshot.exists() {
            return Err(Error::NotFound);
        }
        // Apply only safe, validated updates
        transaction.update(&doc_path, json!({ "status": "processed" }));
        Ok(())
    }).await
}

This prevents attackers from forging document paths and ensures the caller can only modify documents they own, aligning with BOLA/IDOR protections.

3. Reject malformed or excessive payloads

Add size and schema validation before touching Firestore:

async fn validated_webhook(
    webhook_data: web::Json,
) -> Result<impl Responder, Error> {
    // Reject if payload is too large
    if webhook_data.to_string().len() > 10_000 {
        return Err(error::ErrorBadRequest("Payload too large"));
    }
    // Enforce required fields
    if webhook_data.get("local_id").and_then(|v| v.as_str()).is_none() {
        return Err(error::ErrorBadRequest("Missing local_id"));
    }
    Ok(webhook_data.into_inner())
}

These steps reduce the attack surface and make runtime findings less likely, which aligns with the remediation guidance provided by middleBrick’s scans.

Frequently Asked Questions

Why does webhook input validation matter for Firestore paths in Actix?
Unvalidated webhook fields can be used to manipulate Firestore document paths or collection names, enabling BOLA/IDOR abuse. Always treat webhook inputs as untrusted and scope writes to the caller’s identity.
Can middleBrick detect webhook-to-Firestore risks without authentication?
Yes. middleBrick scans the unauthenticated attack surface and can identify indicators such as reflected collection names or document IDs taken directly from webhook payloads, mapping findings to BOLA/IDOR and Property Authorization checks.