HIGH header injectionactixfirestore

Header Injection in Actix with Firestore

Header Injection in Actix with Firestore — how this specific combination creates or exposes the vulnerability

Header Injection occurs when untrusted input is reflected into HTTP response headers without validation or encoding. In an Actix web application that integrates with Google Cloud Firestore, this typically arises when developer-controlled routes construct responses using data taken from query parameters, path segments, or request headers and then forward that data into Firestore document fields or use it to influence response construction. If user-controlled values are placed into headers—either via Firestore document values that are later read and set as headers, or via dynamic route construction that influences Firestore queries and response logic—an attacker can inject newline characters (CRLF sequences) to split the header stream and inject arbitrary headers or body content.

Actix is a Rust web framework where handlers explicitly build responses. If a handler deserializes a Firestore document into a struct and then sets fields as headers (e.g., X-User-Id, X-Trace-Id), unsanitized input stored in Firestore can lead to injected headers. For example, a Firestore document might store a user-supplied label that the Actix handler later reads and sets via HttpResponse::Ok().insert_header((header::X_CUSTOM, value)). If value contains characters like \r\n, an attacker can inject a new header or prematurely end the header section, potentially enabling HTTP response splitting, cache poisoning, or misleading security headers.

The Firestore side does not perform header injection itself, but it can act as a persistence layer for attacker-controlled strings that later influence header construction in Actix. If Firestore documents store unvalidated metadata—such as custom claim values, tags, or user-defined fields—and an Actix service uses those fields to set response headers, the combination creates a two-stage injection path: attacker writes malicious string to Firestore, and the Actix service reflects it into HTTP headers. Additionally, if Firestore document IDs or field values are used to influence query routing or response formatting (e.g., selecting a Firestore collection name based on a header-derived parameter), an attacker may leverage newline injection to manipulate Firestore queries in ways that affect which data is returned, further complicating the trust boundary between the database and the web framework.

To detect this pattern, scans check whether response headers are built from Firestore-derived data without canonicalization or allow-listing. An example unsafe pattern in Actix would be reading a string field from a Firestore document and directly inserting it into a header:

use actix_web::{web, HttpResponse, Result};
use google_cloud_firestore::client::Client;

async fn get_user_header(data: web::Path, client: web::Data) -> Result {
    let doc_id = data.into_inner();
    let doc = client.collection("users").doc(&doc_id).get().await?;
    let custom_value: String = doc.get("x_custom").unwrap_or(&"".to_string());
    // Risk: custom_value may contain CRLF if stored from untrusted input
    Ok(HttpResponse::Ok()
        .insert_header(("X-Custom", custom_value.as_str()))
        .finish())
}

An attacker who can write a Firestore document field x_custom containing foo\r\nX-Injected: 1 can cause the response to include an additional header X-Injected: 1. This illustrates why Firestore-sourced data must be sanitized before use in headers: normalize whitespace, reject or encode CRLF characters, and avoid using user-controlled document fields for header values.

Firestore-Specific Remediation in Actix — concrete code fixes

Remediation focuses on strict validation and encoding of any data derived from Firestore before it is used in HTTP headers. Do not directly insert Firestore document fields into headers. Instead, apply allow-listing, canonicalization, and strict type mapping. For string fields that must be reflected into headers, remove or replace newline characters and enforce length limits.

Below are secure Actix patterns that avoid header injection when working with Firestore data. The first example shows safe header construction by sanitizing Firestore-derived strings:

use actix_web::{web, HttpResponse, Result, http::header::HeaderValue};
use google_cloud_firestore::client::Client;

fn sanitize_header_value(input: &str) -> Option {
    // Reject or strip CR/LF to prevent header injection
    if input.contains('\r') || input.contains('\n') {
        return None;
    }
    HeaderValue::from_str(input).ok()
}

async fn get_user_header_safe(data: web::Path, client: web::Data) -> Result {
    let doc_id = data.into_inner();
    let doc = client.collection("users").doc(&doc_id).get().await?;
    let custom_value: String = doc.get("x_custom").unwrap_or(&"".to_string());
    match sanitize_header_value(&custom_value) {
        Some(safe) => Ok(HttpResponse::Ok().insert_header(("X-Custom", safe)).finish()),
        None => Ok(HttpResponse::BadRequest().body("Invalid header value")),
    }
}

The second example demonstrates using an allow-list approach when the Firestore document dictates which header name is used, ensuring only known-safe header names are used:

use actix_web::{web, HttpResponse, Result};
use google_cloud_firestore::client::Client;
use std::collections::HashSet;

async fn get_typed_header(data: web::Path<(String, String)>, client: web::Data) -> Result {
    let (doc_id, requested_key) = data.into_inner();
    // Allow-list header keys to avoid injection via Firestore-controlled header names
    let allowed_keys: HashSet<&str> = ["x-trace-id", "x-request-id"].iter().cloned().collect();
    if !allowed_keys.contains(requested_key.as_str()) {
        return Ok(HttpResponse::BadRequest().body("Unsupported header key"));
    }
    let doc = client.collection("metadata").doc(&doc_id).get().await?;
    let value: String = doc.get(&requested_key).unwrap_or(&"".to_string());
    // Further sanitize value as shown earlier
    if value.contains('\r') || value.contains('\n') {
        return Ok(HttpResponse::BadRequest().body("Invalid header value"));
    }
    let header_value = actix_web::http::header::HeaderValue::from_str(&value).unwrap_or_default();
    Ok(HttpResponse::Ok()
        .insert_header((requested_key, header_value))
        .finish())
}

For Firestore document IDs used in queries, prefer strict format checks (e.g., UUID or numeric IDs) rather than raw string passthrough. If you must use document metadata for routing or labeling, map Firestore fields to a controlled set of values or enums, and never directly concatenate user input into header strings or query selectors. The middleBrick CLI can be used to scan Actix endpoints that integrate with Firestore for header injection risks, and the GitHub Action can enforce thresholds to prevent insecure patterns from reaching production.

Frequently Asked Questions

Can Firestore itself prevent header injection if I store CRLF in document fields?
Firestore does not sanitize or block newline characters in stored strings. It is the application layer—such as the Actix handler—that must validate and encode data before using it in HTTP headers. Relying on the database to prevent header injection is insufficient.
How can I test my Actix + Firestore integration for header injection without a scanner?
You can write targeted unit tests that supply CRLF sequences in Firestore mock documents and verify that your handler either rejects them or produces sanitized headers. For broader coverage, use the middleBrick CLI to scan your endpoints and confirm that user-controlled Firestore-derived fields are not reflected into headers.