HIGH hallucination attacksaxumfirestore

Hallucination Attacks in Axum with Firestore

Hallucination Attacks in Axum with Firestore — how this specific combination creates or exposes the vulnerability

Hallucination attacks in an Axum service that uses Firestore as a backend occur when an application returns plausible but incorrect or fabricated data in response to queries. This risk is elevated when Firestore documents are used to seed language model prompts or when responses are generated without strict verification against the authoritative data store.

In Axum, handlers often deserialize Firestore documents into Rust structs and then pass those structs into prompt templates or directly into an LLM inference path. If the handler does not validate that the returned fields match the stored document—for example, omitting a version or updated_at timestamp—an attacker can inject or omit fields via malicious parameters, leading the model to generate content not grounded in Firestore. This is especially dangerous when Firestore security rules are misconfigured, allowing broader read access than intended, which can expose sensitive context that the model then repeats or hallucinates.

The combination increases exposure because Firestore’s flexible schema can return nested maps and arrays that Axum may flatten into loosely typed structures. If the application trusts this flattened data without schema checks, the LLM may treat incomplete or altered data as authoritative. For instance, a query for user preferences might omit consent flags if the Firestore document is partially constructed, and the model could hallucinate consent where none exists. Attack patterns include prompt injection via crafted inputs that shift the model’s attention to injected Firestore-like text, data exfiltration through verbose model outputs that repeat sensitive Firestore metadata, and privilege escalation by exploiting missing field-level authorization checks before data reaches the model.

Additionally, Firestore index behavior and eventual consistency can cause Axum handlers to read stale data. An attacker may exploit this lag to submit updates that do not immediately reflect, leading the handler to serve outdated context and the LLM to generate responses inconsistent with the latest state. Without runtime assertions that compare model outputs against the exact Firestore document state, these discrepancies become covert channels for misinformation.

Firestore-Specific Remediation in Axum — concrete code fixes

To mitigate hallucination attacks, enforce strict validation between Firestore documents and the data presented to the LLM in Axum. Use strongly typed structs, verify timestamps and version fields, and avoid flattening Firestore nested data without checks.

use axum::{routing::get, Router};
use firestore::{FirestoreDb, structs::Document};
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;

#[derive(Debug, Clone, Serialize, Deserialize)]
struct UserPreferences {
    user_id: String,
    theme: String,
    consent_given: bool,
    version: i64,
    updated_at: chrono::DateTime,
}

async fn get_preferences(
    db: FirestoreDb,
    user_id: String,
) -> Result {
    let doc_ref = db.doc(&format!("users/{}", user_id));
    let doc: Document = doc_ref.get().await.map_err(|e| {
        (axum::http::StatusCode::INTERNAL_SERVER_ERROR, e.to_string())
    })?;

    // Ensure the document exists and the version matches expectations.
    let prefs = doc.into_inner().ok_or_else(|| {
        (axum::http::StatusCode::NOT_FOUND, "Preferences not found".to_string())
    })?;

    // Critical: validate freshness and integrity before using in prompts.
    if prefs.version < 1 {
        return Err((axum::http::StatusCode::CONFLICT, "Invalid version".to_string()));
    }

    // Reject if updated_at is too old to reduce reliance on potentially stale cache.
    let freshness = chrono::Utc::now() - prefs.updated_at;
    if freshness > chrono::Duration::minutes(5) {
        return Err((axum::http::StatusCode::REQUEST_TIMEOUT, "Stale data".to_string()));
    }

    Ok(prefs)
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let db = FirestoreDb::new("my-project-id").await?;
    let app = Router::new().route("/preferences/:user_id", get(|user_id| get_preferences(db.clone(), user_id)));
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    axum::Server::bind(&addr).serve(app.into_make_service()).await?;
    Ok(())
}

In this example, the handler retrieves a Firestore document into a strongly typed UserPreferences struct and validates version and updated_at before the data is used. This ensures that the LLM receives only verified, up-to-date fields, reducing the chance of hallucination. For endpoints that generate responses with LLMs, compare the model output against the canonical Firestore fields and log mismatches for audit. If using the middleBrick dashboard, you can track schema drift and anomalies over time; the CLI can automate checks in scripts, and the GitHub Action can enforce that no deployment proceeds if validation logic is missing. The MCP server allows you to run these checks directly from your IDE while writing the handler code.

Related CWEs: llmSecurity

CWE IDNameSeverity
CWE-754Improper Check for Unusual or Exceptional Conditions MEDIUM

Frequently Asked Questions

How does Firestore’s flexible schema increase hallucination risk in Axum handlers?
Firestore allows nested maps and arrays with optional fields. If Axum handlers flatten this data into loosely typed structures and pass it to an LLM without strict schema validation, the model may treat incomplete or altered data as authoritative and generate content not grounded in the stored document.
Why is timestamp and version validation important when using Firestore with Axum?
Firestore’s eventual consistency and index behavior can return stale data. Validating version and updated_at ensures the handler uses fresh, authoritative data before sending it to the LLM, reducing hallucination caused by outdated or conflicting document states.