HIGH nosql injectionaxumfirestore

Nosql Injection in Axum with Firestore

Nosql Injection in Axum with Firestore — how this specific combination creates or exposes the vulnerability

NoSQL injection occurs when untrusted input is incorporated into database queries without proper validation or parameterization. In an Axum application that uses Google Cloud Firestore, this typically manifests through user-controlled values being concatenated into query filters or document paths. Firestore’s query language is not SQL, but it still accepts structured filters that can be manipulated if input is not strictly constrained.

Consider an endpoint that retrieves user profiles by a document identifier provided via a query parameter. If the route parameter is directly used to construct a Firestore collection reference or a where filter, an attacker can supply crafted input such as users/../../admin or a map key like {"__name__": "users/../../../admin"}. Because Axum deserializes path segments into Rust data structures before passing them to business logic, improperly sanitized inputs can shift the query scope or bypass intended access controls.

Another common pattern involves dynamic field-based filtering where a client specifies which fields to query. If the field name is taken directly from user input, an attacker may inject logical operators or traversal patterns that change the semantics of the query. For instance, a filter key like {"$where": "true"} could be interpreted by a less strict intermediary layer as a logical condition that always evaluates to true, effectively returning all documents in a collection. While Firestore itself does not evaluate arbitrary JavaScript, the surrounding Axum service may apply transformations or pass the input to other services that do, expanding the attack surface.

Additionally, Firestore security rules are not a substitute for input validation at the application layer. Rules are enforced after the query reaches the backend, meaning a malicious request can still traverse through Axum, consume processing time, and generate logs or errors before being denied. This can lead to denial-of-service conditions or information leakage through error messages if the application does not handle failures gracefully.

The combination of Axum’s type-driven routing and Firestore’s flexible document model increases the importance of strict schema validation and explicit allow-listing of query parameters. Without it, the boundary between application logic and data storage blurs, enabling injection-style attacks that parallel classic injection classes in SQL contexts.

Firestore-Specific Remediation in Axum — concrete code fixes

To mitigate NoSQL injection risks in an Axum service using Firestore, adopt strict input validation, explicit schema checks, and parameterized query construction. Avoid string concatenation or dynamic map building based on raw user input. Instead, use strongly typed structures and enforce allow-listing at every layer.

Here is an example of a vulnerable Axum handler that directly uses a path parameter to form a Firestore document reference:

async fn get_user(
    Path(user_id): Path,
    firestore: Extension,
) -> Result<Json<User>, StatusCode> {
    let doc_ref = firestore.collection("users").doc(&user_id);
    let snapshot = doc_ref.get().await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
    if let Some(data) = snapshot.get("data") {
        let user: User = data.clone().try_into().map_err(|_| StatusCode::BAD_REQUEST)?;
        Ok(Json(user))
    } else {
        Err(StatusCode::NOT_FOUND)
    }
}

In this example, user_id is taken directly from the URL path and appended to a document path. An attacker could supply a value such as users/../../../admin depending on routing configuration, potentially accessing unintended documents.

A safer approach validates and normalizes the input before constructing any Firestore reference:

async fn get_user_safe(
    Path(user_id): Path,
    firestore: Extension<FirestoreDb>,
) -> Result<Json<User>, StatusCode> {
    // Allow-list alphanumeric user IDs with a fixed prefix
    let validated_id = sanitize_user_id(&user_id).ok_or(StatusCode::BAD_REQUEST)?;
    let doc_ref = firestore.collection("users").doc(&validated_id);
    let snapshot = doc_ref.get().await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
    snapshot_to_user_json(snapshot).ok_or(StatusCode::NOT_FOUND)
}

fn sanitize_user_id(input: &str) -> Option<String> {
    let pattern = regex::Regex::new(r"^user_[a-zA-Z0-9]{1,64}$").ok()?;
    if pattern.is_match(input) {
        Some(input.to_string())
    } else {
        None
    }
}

When building queries with dynamic fields, use a static map of allowed field names instead of echoing user input:

async fn list_users_by_field(
    Query(params): Query<HashMap<String, String>>,
    firestore: Extension<FirestoreDb>,
) -> Result<Json<Vec<User>>, StatusCode> {
    let field = params.get("field").ok_or(StatusCode::BAD_REQUEST)?;
    // Allow-list only known, safe field names
    let allowed = [("email", "email"), ("status", "status")];
    let (db_field, _alias) = allowed.iter().find(|(k, _)| *k == &field).ok_or(StatusCode::BAD_REQUEST)?;

    let query = firestore.collection("users").where_eq(db_field, ¶ms.get("value").ok_or(StatusCode::BAD_REQUEST)?);
    let snapshot = query.get().await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
    let users = snapshot.iter().filter_map(snapshot_to_user_json).collect();
    Ok(Json(users))
}

In both cases, the application avoids interpreting raw input as code or structure, ensuring that Firestore queries remain predictable and bounded. Combine these practices with runtime monitoring and automated scanning using tools such as the middleBrick CLI to detect NoSQL injection risks during development. The CLI can be invoked with middlebrick scan <url> to integrate checks into local workflows, while the GitHub Action helps enforce security gates in CI/CD pipelines.

Frequently Asked Questions

Can Firestore security rules alone prevent NoSQL injection in Axum?
No. Security rules are enforced after a request reaches Firestore, so malicious input can still pass through Axum and consume resources. Input validation and allow-listing at the application layer are required to prevent injection attempts before they reach the database.
Does middleBrick fix NoSQL injection findings automatically?
No. middleBrick detects and reports security findings, including NoSQL injection risks, with severity and remediation guidance. It does not automatically patch or modify code; developers must apply the suggested fixes in the application.