HIGH log injectionaxumfirestore

Log Injection in Axum with Firestore

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

Log injection occurs when untrusted input is written directly into log entries without validation or sanitization, allowing an attacker to forge log lines, obscure true events, or inject control characters that corrupt log formatting. In an Axum application that uses Firestore as a backend, the risk emerges at the intersection of Axum request handling and Firestore document writes used for logging.

Axum is an asynchronous web framework built on Tower and Hyper, where request handling is composed through extractors and middleware. When developers choose to record application events by writing structured data into Firestore, they often include request-specific fields such as user identifiers, request IDs, or free-form text provided by clients. If these fields are inserted directly into Firestore documents that are later read by log aggregation tools or surfaced via custom dashboards, newline characters (\n or \r\n) or other control sequences in the input can break log line boundaries.

For example, consider an Axum handler that creates a Firestore document to represent an operation event. If a client-supplied parameter like display_name contains a newline, the resulting Firestore document may store that value verbatim. Later, when logs are exported and formatted as line-delimited text, the injected newline causes one logical log entry to span multiple lines. This can mislead monitoring systems and complicate incident response by fragmenting context. Additionally, injection of structured characters such as JSON control sequences can subtly alter how parsers interpret event boundaries, especially when Firestore documents are serialized to JSON for downstream consumption.

The vulnerability is compounded because Firestore documents often include nested maps and arrays. An attacker may supply arrays containing malicious entries or embed control characters within string fields that are indexed and searched by log queries. When logs are replayed or streamed to analysis tools, these injected elements can distort timelines, produce false positives, or trigger parser errors. Because the Axum application trusts client input that ultimately persists in Firestore and is later treated as log data, the attack surface spans the web framework, the database write path, and any log processing pipeline that relies on predictable formatting.

Because middleBrick performs black-box scanning without credentials, it can detect patterns consistent with log injection by analyzing API behavior and exported OpenAPI specifications. When combined with Firestore-driven logging in Axum, the scanner can highlight endpoints that accept untrusted input and subsequently write to data stores used for audit or observability, emphasizing the need for input validation and output encoding before logs are persisted.

Firestore-Specific Remediation in Axum — concrete code fixes

Remediation focuses on ensuring that any data derived from untrusted sources is sanitized before being stored in Firestore documents that function as logs. In Axum, this means validating and transforming request inputs within handlers or dedicated middleware before constructing Firestore writes. Below are concrete patterns using the Firestore SDK for Rust that demonstrate safe logging practices.

1. Sanitize and encode user input before writing to Firestore

Replace newlines and other control characters with safe placeholders. Perform this transformation at the point where raw extractor output is converted into Firestore document fields.

use axum::extract::Query;
use firestore::FirestoreDb;
use serde_json::json;

async fn log_event_handler(
    Query(params): Query<LogParams>,
    db: &FirestoreDb,
) -> Result<(), (StatusCode, String)> {
    // Sanitize: replace newlines and carriage returns
    let sanitized_message = params.message.replace('\n', " ").replace('\r', " ");
    let doc = serde_json::json!({
        "user_id": params.user_id,
        "message": sanitized_message,
        "timestamp": chrono::Utc::now().to_rfc3339(),
    });
    db.create("events", Some(doc)).await.map_err(|e| {
        (StatusCode::INTERNAL_SERVER_ERROR, e.to_string())
    })?;
    Ok(())
}

#[derive(serde::Deserialize)]
struct LogParams {
    user_id: String,
    message: String,
}

2. Use structured logging with explicit field typing

Store log-related data in typed Firestore maps rather than dynamically building documents from raw strings. This reduces the risk of unexpected control flow injection and makes downstream parsing more predictable.

use firestore::types::DocumentData;
use std::collections::HashMap;

fn build_log_document(user_id: &str, action: &str, extra: &HashMap<String, String>) -> DocumentData {
    let mut doc = DocumentData::new();
    doc.insert("user_id", user_id.to_string());
    doc.insert("action", action.to_string());
    // Extra fields are explicitly encoded, avoiding raw concatenation
    for (k, v) in extra {
        doc.insert(k.clone(), v.replace(['\n', '\r'], " "));
    }
    doc
}

3. Validate input length and content at the extractor level

Use Axum extractors with validation layers to reject or transform problematic input before it reaches business logic. This enforces boundaries at the API edge.

use axum::async_trait;
use axum::extract::{FromRequest, Request};
use axum::http::StatusCode;

struct SafeString(pub String);

#[async_trait]
impl FromRequest<S> for SafeString {
    type Rejection = (StatusCode, String);

    async fn from_request(req: Request, _state: &S) -> Result<Self, Self::Rejection> {
        let body = hyper::body::to_bytes(req.into_body()).await.map_err(|e| {
            (StatusCode::BAD_REQUEST, e.to_string())
        })?;
        let text = String::from_utf8(body.to_vec()).map_err(|e| {
            (StatusCode::BAD_REQUEST, e.to_string())
        })?;
        if text.contains('\n') || text.contains('\r') {
            return Err((StatusCode::BAD_REQUEST, "Control characters not allowed".to_string()));
        }
        Ok(SafeString(text))
    }
}

By applying these patterns, an Axum service that writes to Firestore can ensure that log entries remain structurally sound and that injected newlines or other control sequences do not corrupt log formatting. Because Firestore documents are often used as an append-only audit trail, these mitigations help preserve the integrity of observability data without requiring changes to log consumers.

Frequently Asked Questions

Can middleBrick detect log injection risks in APIs that write to Firestore from Axum?
Yes. middleBrick scans unauthenticated attack surfaces and OpenAPI specifications, identifying endpoints that accept untrusted input and tracing how that data is persisted. It can highlight patterns consistent with log injection by correlating input fields with Firestore write operations, though it does not fix or block findings.
Does remediation require changes to Firestore security rules?
Not primarily. Because log injection is a data validation issue at the application layer, fixes are implemented in Axum handlers and middleware that sanitize input before Firestore writes. Security rules can complement these efforts but do not replace input sanitization and encoding.