Excessive Data Exposure in Actix with Firestore
Excessive Data Exposure in Actix with Firestore — how this specific combination creates or exposes the vulnerability
Excessive Data Exposure occurs when an API returns more information than necessary for a given operation, such as full database records that include sensitive fields not required by the client. In an Actix application using Google Cloud Firestore as the backend, this risk is amplified by common patterns that retrieve entire documents and serialize them directly into JSON responses without field-level filtering.
When an endpoint in Actix performs a Firestore get or query and encodes the resulting DocumentSnapshot into a response using generic serialization (e.g., via serde), all mapped fields are included by default. This can inadvertently expose internal metadata, operational fields, or personally identifiable information (PII) such as email addresses, phone numbers, or internal identifiers. For example, a user profile endpoint might map a Firestore document to a struct that includes fields like password_hash, reset_token, or internal_notes. If the struct does not explicitly exclude these fields, serialization can expose them to the client.
Additionally, Firestore documents often contain nested maps and arrays that may include sensitive subcollections or metadata. In Actix, if the response is constructed by directly marshaling the raw Firestore document without pruning or transformation, the API surface becomes larger than intended. This increases the impact of other issues such as broken object-level authorization (BOLA), as more data is returned than the caller should reasonably access.
The interaction between Actix's asynchronous request handling and Firestore's document model can also contribute to exposure. Firestore queries may return more documents than intended if filters are not applied server-side, and Actix handlers might stream or batch these results without enforcing strict field selection. Without explicit projection or field masking, the serialized output may reveal sensitive data embedded in indexed fields or stored as part of the document's properties.
To detect this pattern, scans evaluate whether responses include unexpected or sensitive fields, whether Firestore queries are overly broad, and whether the application enforces field-level controls. Findings often highlight missing field filtering, missing struct tags to omit sensitive data, and lack of server-side query constraints that limit returned data to only what is required by the client.
Firestore-Specific Remediation in Actix — concrete code fixes
Remediation focuses on ensuring that only the necessary data is retrieved from Firestore and that the serialized response contains strictly required fields. This involves using Firestore query projections, explicit document parsing, and selective serialization in Actix handlers.
First, use Firestore queries that select only the fields you need. Instead of retrieving full documents and relying on struct mapping to exclude fields, request specific field paths. This reduces network traffic and ensures that sensitive fields never reach the Actix runtime.
use google_cloud_rust::firestore::client::Client;
use google_cloud_rust::firestore::document::Document;
async fn get_user_profile(user_id: &str, client: &Client) -> Result<UserPublicProfile, Error> {
// Request only required fields from Firestore
let docs = client
.collection("users")
.select(&["display_name", "avatar_url", "created_at"])
.doc(user_id)
.get()
.await?;
// Manually map to a minimal public DTO
let doc = docs.document;
let data = doc.fields.ok_or(Error::MissingData)?;
let display_name = data.get("display_name").and_then(|v| v.as_string()).ok_or(Error::InvalidField)?;
let avatar_url = data.get("avatar_url").and_then(|v| v.as_string()).ok_or(Error::InvalidField)?;
let created_at = data.get("created_at").and_then(|v| v.as_timestamp()).ok_or(Error::InvalidField)?;
Ok(UserPublicProfile {
display_name,
avatar_url,
created_at,
})
}
Second, define strict Data Transfer Objects (DTOs) in Actix that omit sensitive fields and use Serde attributes to ensure no accidental leakage occurs. Even if a field exists in the Firestore document, it will not be serialized if it is not present in the DTO.
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
pub struct UserPublicProfile {
pub display_name: String,
pub avatar_url: String,
pub created_at: chrono::DateTime<chrono::Utc>,
}
#[derive(Serialize)]
pub struct UserPrivateProfile {
pub display_name: String,
pub email: String,
#[serde(skip_serializing)]
pub password_hash: String,
#[serde(skip_serializing)]
pub reset_token: Option<String>,
}
Third, avoid automatic mapping of Firestore documents to internal domain models that include sensitive attributes. Instead, parse documents explicitly and construct responses from validated subsets of data. This prevents unintended exposure of fields such as internal identifiers or administrative flags.
Finally, enforce server-side constraints in Firestore queries to limit document scope. Combine these techniques with Actix's guard and extractor patterns to ensure that only authenticated and authorized requests can access endpoints that retrieve or modify data, reducing the blast radius of any potential exposure.
Related CWEs: propertyAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-915 | Mass Assignment | HIGH |