HIGH pii leakageaxumfirestore

Pii Leakage in Axum with Firestore

Pii Leakage in Axum with Firestore — how this specific combination creates or exposes the vulnerability

When building a Rust API service with Axum and persisting data in Google Cloud Firestore, PII leakage commonly arises from two dimensions: the Firestore client configuration and the application-level data handling in route handlers. Firestore stores documents as JSON-like maps, and if your Axum handlers write or query Firestore without normalizing, filtering, or masking sensitive fields, PII such as email addresses, phone numbers, or government IDs can be stored in clear text and returned to clients.

In a typical Axum handler, developers deserialize an incoming JSON payload into a Rust struct, then directly map fields into a Firestore document. If the struct contains fields like email or ssn and these are persisted without redaction, Firestore retains them. Subsequent reads—whether via the Axum route that fetches a document by ID or through Firestore queries used to populate response payloads—can inadvertently expose this data. For example, a GET route that retrieves a user document and returns it as JSON may serialize the entire Firestore document, including sensitive fields, unless explicit filtering is applied.

The Firestore security rules are not enforced by the Axum runtime; rules are a separate control plane mechanism. If rules are misconfigured or bypassed (for example, during development where rules are open), an attacker who compromises an endpoint or gains network access might read or write PII directly. Additionally, Firestore indexes can expose metadata; if an index includes a sensitive field and is queried without restriction, the index itself becomes a potential avenue for inference or enumeration attacks.

With middleBrick’s OpenAPI/Swagger analysis, such integrations are scanned against the unauthenticated attack surface. The scanner checks whether endpoints that interact with Firestore expose PII in responses or allow over-permissive data flows. Findings include improper schema definitions, missing server-side filters, and risky rule configurations. The LLM/AI Security checks further probe whether system prompts or logs might leak PII through AI-assisted tooling that inspects or comments on Firestore schemas, and output scanning ensures generated code or logs do not surface sensitive data.

Remediation guidance centers on minimizing the data footprint in Firestore documents returned to or stored by Axum handlers, applying strict schema validation, and using selective serialization. The scanner maps findings to frameworks like OWASP API Top 10 and GDPR, emphasizing that detection is provided without automatic fixes; developers must implement filtering and access controls.

Firestore-Specific Remediation in Axum — concrete code fixes

To prevent PII leakage, treat Firestore documents as a persistence format distinct from your API contract. Define separate request/response structs for Axum and use explicit mapping to strip or mask sensitive fields before and after Firestore interactions.

Example: store only necessary data in Firestore and omit PII from responses.

use axum::{routing::get, Router};
use serde::{Deserialize, Serialize};
use google_cloud_firestore::client::Client;
use std::sync::Arc;

// Domain model — keep PII in your internal representation if needed.
#[derive(Debug, Clone)]
struct UserRecord {
    pub id: String,
    pub email: String,
    pub ssn: String,
}

// API request DTO — validate but do not persist PII directly.
#[derive(Debug, Deserialize)]
struct CreateUserRequest {
    email: String,
    ssn: String,
    display_name: String,
}

// API response DTO — exclude PII.
#[derive(Debug, Serialize)]
struct UserResponse {
    id: String,
    display_name: String,
    // email intentionally omitted.
}

async fn create_user(
    client: Arc<Client>,
    req: CreateUserRequest,
) -> Result<UserResponse, (axum::http::StatusCode, String)> {
    // Optionally validate and transform before storing.
    let doc_id = uuid::Uuid::new_v4().to_string();
    // Store only necessary fields; avoid persisting PII if not required.
    let payload = serde_json::json!({
        "display_name": req.display_name,
        // Consider storing a normalized version or hash if needed.
    });
    client
        .create_doc(&["users", &doc_id], &payload, None)
        .await
        .map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    Ok(UserResponse {
        id: doc_id,
        display_name: req.display_name,
    })
}

async fn get_user(
    client: Arc<Client>,
    user_id: String,
) -> Result<UserResponse, (axum::http::StatusCode, String)> {
    let doc = client.get_doc(&["users", &user_id])
        .await
        .map_err(|e| (axum::http::StatusCode::NOT_FOUND, e.to_string()))?;

    // Firestore document may contain extra fields; explicitly select safe fields.
    let display_name = doc.fields()
        .and_then(|f| f.get("display_name"))
        .and_then(|v| v.as_str())
        .ok_or_else(|| (axum::http::StatusCode::BAD_REQUEST, "missing display_name".to_string()))?;

    Ok(UserResponse {
        id: user_id,
        display_name: display_name.to_string(),
    })
}

// In main, wire routes with shared client.
#[tokio::main]
async fn main() {
    let client = Arc::new(Client::new().await.unwrap());
    let app = Router::new()
        .route("/users", get(|axum::extract::State(client)| async move { /* list safe fields */ }))
        .with_state(client);
    // axum::Server::bind(&...).serve(app).await.unwrap();
}

Key practices:

  • Never forward Firestore documents directly as API responses; map to a DTO that excludes PII.
  • Apply server-side validation and normalization on input before persisting to Firestore.
  • Use Firestore field masks or explicit projection when reading to limit returned fields.
  • Review Firestore rules to ensure they align with least-privilege access; remember rules are complementary to application-level controls.
  • Audit logs and index usage: ensure sensitive fields are not inadvertently indexed or exposed through logging integrations that Axum might feed.

middleBrick’s scans validate whether your OpenAPI spec and runtime behavior align with these practices, highlighting endpoints that expose raw Firestore documents or lack output filtering. The CLI and GitHub Action integrations can be used to enforce security gates in CI/CD, while the Dashboard tracks changes over time.

Related CWEs: dataExposure

CWE IDNameSeverity
CWE-200Exposure of Sensitive Information HIGH
CWE-209Error Information Disclosure MEDIUM
CWE-213Exposure of Sensitive Information Due to Incompatible Policies HIGH
CWE-215Insertion of Sensitive Information Into Debugging Code MEDIUM
CWE-312Cleartext Storage of Sensitive Information HIGH
CWE-359Exposure of Private Personal Information (PII) HIGH
CWE-522Insufficiently Protected Credentials CRITICAL
CWE-532Insertion of Sensitive Information into Log File MEDIUM
CWE-538Insertion of Sensitive Information into Externally-Accessible File HIGH
CWE-540Inclusion of Sensitive Information in Source Code HIGH

Frequently Asked Questions

How can I ensure Firestore-stored PII is not exposed through Axum endpoints?
Use separate API DTOs that omit PII, apply server-side field selection when reading from Firestore, and avoid forwarding raw Firestore documents to responses. Validate and normalize inputs before persistence.
Does Firestore encryption at rest protect me from PII leakage in Axum responses?
Encryption at rest protects stored data, but it does not prevent PII from being included in API responses if your Axum handlers serialize entire Firestore documents. Application-level filtering is required.