Header Injection in Axum with Firestore
Header Injection in Axum with Firestore — how this specific combination creates or exposes the vulnerability
Header Injection occurs when user-controlled data is placed into HTTP response headers without proper validation or encoding. In an Axum application that integrates with Google Cloud Firestore, this typically happens when request headers (e.g., X-Forwarded-For, Referer, or custom headers such as X-Client-Email) are read and then used to construct Firestore document IDs, collection names, or Firestore metadata fields that are later reflected in responses or logs.
Because Axum is a Rust web framework that does not automatically sanitize header values, developers must explicitly validate and sanitize any header before using it in Firestore operations. If a header value is used to dynamically select a Firestore collection (for example, storing tenant data in {header_value}/users/{user_id}) and that header is attacker-controlled, an attacker can inject newline characters or other delimiters to move outside the intended scope. This can lead to BOLA/IDOR via path traversal across logical Firestore boundaries or unintended data access across tenants.
Additionally, Firestore field names and document IDs derived from headers may be reflected in the Axum response body or logs. If an attacker can inject crafted header values, they may attempt to probe internal naming conventions or cause log injection, which can complicate monitoring and incident response. Although Firestore itself does not interpret newline characters in document IDs (it treats them as literal characters), an Axum application that concatenates headers into Firestore paths without canonicalization increases the risk of malformed paths and inconsistent authorization checks.
The risk is compounded when the application uses unauthenticated Firestore rules for read-only endpoints and reflects header-derived values in JSON responses. An attacker can use Header Injection to manipulate which Firestore collections or documents are targeted, potentially accessing data belonging to other users by injecting path segments that change the logical resource identifier. Because middleBrick tests unauthenticated attack surfaces, such misconfigurations are detectable when header values influence Firestore query targets without strict allowlisting.
Real-world examples include using the X-Requested-With header to decide whether to read from a "public" or "private" Firestore collection, or using the Authorization header (when improperly parsed) to infer tenant context. In each case, missing input validation on the header enables attackers to manipulate Firestore access patterns, which aligns with BOLA and Property Authorization checks that middleBrick evaluates across the API surface.
Firestore-Specific Remediation in Axum — concrete code fixes
To prevent Header Injection in Axum when working with Firestore, treat all incoming headers as untrusted. Validate, normalize, and scope header-derived values before using them in Firestore document paths or queries. Below are concrete, idiomatic examples using the Firestore Rust SDK and Axum extractors.
1. Strict Allowlist for Header-Driven Context
Do not directly use a header value to determine Firestore collection names. Instead, map known values to canonical identifiers.
use axum::{extract::Header, http::HeaderName};
use firestore::FirestoreDb;
async fn get_tenant_collection(
Header(tenant): Header<String>,
) -> Result<FirestoreDb, (axum::http::StatusCode, String)> {
// Allowlist known tenant identifiers; reject anything unexpected
let allowed = ["acme-corp", "beta-labs"];
if !allowed.contains(&tenant.as_str()) {
return Err((axum::http::StatusCode::BAD_REQUEST, "invalid tenant".into()));
}
let db = FirestoreDb::new(tenant).await.map_err(|e| {
(axum::http::StatusCode::INTERNAL_SERVER_ERROR, e.to_string())
})?;
Ok(db)
}
2. Sanitize and Canonicalize Header-Derived IDs
If a header must influence a Firestore document ID, sanitize it by removing or replacing control characters and delimiters that could affect path resolution or logging.
use regex::Regex;
fn sanitize_id(value: &str) -> String {
// Remove characters that may interfere with path handling or log parsing
let re = Regex::new(r"[^a-zA-Z0-9\-_]").unwrap();
re.replace_all(value, "_").to_string()
}
async fn user_document_id(user_header: String) -> String {
let safe = sanitize_id(&user_header);
format!("users/{}", safe)
}
3. Isolate Tenant Scope at Query Time
Do not rely on client-supplied path components to select collections. Instead, derive tenant context from a verified source (e.g., JWT claims) and enforce it server-side in Firestore queries.
use firestore::FirestoreDb;
use axum::{extract::State};
struct AppState {
firestore: FirestoreDb,
tenant_id: String, // derived from authentication, not headers
}
async fn list_users(State(state): State<AppState>) -> Result<impl axum::response::IntoResponse, (axum::http::StatusCode, String)> {
// Query a fixed, server-defined collection path
let users: Vec<User> = state.firestore
.collection(&format!("tenants/{}/users", state.tenant_id))
.get_documents()
.await
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
Ok(axum::Json(users))
}
4. Avoid Reflecting Untrusted Headers in Responses
If your API returns header-derived values, ensure they are escaped or omitted. Never directly echo an attacker-controlled header into a JSON body that may be interpreted as active content.
use axum::Json;
use serde::Serialize;
#[derive(Serialize)]
struct SafeResponse {
user_id: String,
// Do not include raw header values here
}
async fn make_response(user_id: String) -> Json<SafeResponse> {
Json(SafeResponse {
user_id,
})
}
By combining allowlisting, canonicalization, and server-side scoping, you mitigate Header Injection risks in Axum applications that use Firestore. These practices reduce the likelihood of BOLA/IDOR and ensure that Firestore access patterns remain predictable and auditable.