MEDIUM mass assignmentaxumfirestore

Mass Assignment in Axum with Firestore

Mass Assignment in Axum with Firestore — how this specific combination creates or exposes the vulnerability

Mass assignment occurs when an API binds incoming HTTP request data directly to data model fields without sufficient filtering. In an Axum application using Google Cloud Firestore as a backend, this typically involves deserializing JSON into a Rust struct and then writing that struct to Firestore. Because Firestore documents are often represented as maps or structs that mirror application models, indiscriminately assigning request fields to Firestore document fields can allow attackers to set sensitive or immutable properties such as admin, role, created_at, or billing-related fields.

Axum does not perform automatic field filtering during deserialization; developers must explicitly select which fields to bind. If a handler uses a catch-all deserialization pattern (e.g., relying on serde to map all JSON keys to struct fields) and then writes that struct directly into Firestore, an attacker can supply extra keys in the request body that modify privileged fields. For example, an attacker could include "role": "super_admin" in a user profile update request and have those changes persisted to Firestore if the handler does not sanitize input.

The Firestore client in Rust is typically used via the google-cloud-rust or community crates, where a document is represented as a map or a strongly typed struct. When combined with Axum extractors such as Json<T>, the risk arises if the handler passes the deserialized type directly into a Firestore set operation without removing or ignoring untrusted fields. Because Firestore operations are asynchronous and batched by default in many client configurations, mass assignment vulnerabilities can lead to privilege escalation, data tampering, or unauthorized access across multiple documents.

Consider a user profile update endpoint that accepts a JSON payload and writes it to a Firestore user document without field filtering:

// UNSAFE: direct struct deserialization and Firestore write
#[derive(Deserialize)]
struct UpdateUser {
    display_name: Option<String>,
    email: Option<String>,
    // Missing: role, is_admin, permissions, etc.
}

async fn update_profile(
    user_id: Path<String>,
    json: Json<UpdateUser>,
    db: Extension<FirestoreDb>
) -> Result<impl IntoResponse, (StatusCode, String)> {
    let doc_ref = db.collection("users").doc(user_id.into_inner());
    doc_ref.set(json.into_inner()).await.map_err(|e| {
        (StatusCode::INTERNAL_SERVER_ERROR, e.to_string())
    })?;
    Ok((StatusCode::OK, "updated"))
}

If a client sends additional fields such as "role": "admin", serde will ignore them due to #[serde(deny_unknown_fields)] being absent, and the struct will not contain them. However, if the handler uses a HashMap<String, Value> or a more permissive deserialization strategy, those extra fields can be written directly to Firestore. This demonstrates why explicit field selection and schema validation are essential when combining Axum extractors with Firestore writes.

Mass assignment in this context is not limited to structs; it also affects dynamic document updates using Firestore's update or set with map-like data. Attackers may leverage nested objects or arrays to escalate impact, especially if Firestore security rules are misconfigured or absent. Therefore, validating and sanitizing input at the handler level is critical to preventing unauthorized modifications in Axum + Firestore applications.

Firestore-Specific Remediation in Axum — concrete code fixes

To prevent mass assignment in an Axum application using Firestore, explicitly define which fields are allowed and construct Firestore documents from a sanitized subset of the incoming data. Avoid passing raw deserialized structs or maps directly to Firestore operations. Instead, map validated fields to Firestore document formats programmatically.

One approach is to define a minimal DTO (data transfer object) that includes only safe, expected fields and then convert it into a Firestore-compatible document map or entity. Here is a secure example in Rust using Axum and Firestore:

// SAFE: explicit field selection and Firestore document construction
#[derive(Deserialize)]
struct UpdateUserDto {
    display_name: Option<String>,
    email: Option<String>,
}

struct UserDocument {
    display_name: Option<String>,
    email: Option<String>,
    updated_at: Timestamp,
}

async fn update_profile(
    user_id: Path<String>,
    json: Json<UpdateUserDto>,
    db: Extension<FirestoreDb>
) -> Result<impl IntoResponse, (StatusCode, String)> {
    let dto = json.into_inner();
    let doc_ref = db.collection("users").doc(user_id.into_inner());

    // Build a Firestore-compatible map with only trusted fields
    let mut update_data = std::collections::HashMap::new();
    if let Some(name) = dto.display_name {
        update_data.insert("display_name", name);
    }
    if let Some(email) = dto.email {
        update_data.insert("email", email);
    }
    update_data.insert("updated_at", Timestamp::now());

    doc_ref.update(update_data).await.map_err(|e| {
        (StatusCode::INTERNAL_SERVER_ERROR, e.to_string())
    })?;
    Ok((StatusCode::OK, "updated"))
}

In this example, only display_name and email are accepted from the request. The Firestore update is performed using a map that explicitly includes only these fields plus a server-side timestamp. This approach ensures that privileged fields such as role, is_admin, or billing metadata cannot be modified through the API regardless of whether they are present in the request.

For strongly typed workflows, implement a conversion layer that maps the DTO to a Firestore entity while omitting sensitive fields. You can also leverage Firestore document transforms (e.g., server timestamps) instead of client-provided values to further reduce risk. Combining these patterns with schema validation libraries and strict content-type checks enhances protection against mass assignment across Axum and Firestore integrations.

Related CWEs: propertyAuthorization

CWE IDNameSeverity
CWE-915Mass Assignment HIGH

Frequently Asked Questions

Can using a catch-all JSON extractor in Axum increase mass assignment risk with Firestore?
Yes. Using a catch-all or loosely defined serde deserialization (such as HashMap<String, Value> without validation) allows attackers to inject unexpected fields that may be written directly to Firestore. Prefer explicit DTOs and field-level validation.
Does middleBrick detect mass assignment risks in API scans involving Firestore and Axum?
middleBrick scans unauthenticated attack surfaces and tests endpoints for common vulnerabilities including improper input validation and authorization issues. While it does not infer implementation language specifics, its checks for property authorization and input validation can surface indicators related to mass assignment when endpoints accept and persist untrusted data.