Path Traversal in Axum with Firestore
Path Traversal in Axum with Firestore — how this specific combination creates or exposes the vulnerability
Path Traversal occurs when user-supplied input is used to construct file or document paths without proper validation, allowing an attacker to access files or resources outside the intended directory or collection. In an Axum application that uses Google Cloud Firestore as a backend, this typically manifests through document ID or field values that are concatenated into paths or queries without sanitization. Because Firestore organizes data into collections and documents, a developer might directly use a user-controlled string as a document ID or part of a collection path. If that string contains sequences like ../ or encoded variants, an attacker can attempt to traverse logical boundaries between Firestore collections or documents that should be isolated.
For example, an endpoint like /users/{user_id}/profile might read a Firestore document using a direct mapping from the URL parameter to a document ID. If the application does not validate or sanitize user_id, an attacker could supply a value such as ../../../admin/settings. While Firestore does not use a traditional filesystem path, the logical hierarchy of collections and documents can be abused: a crafted ID may resolve to a document in a privileged collection (e.g., admin/settings) if the application code does not enforce strict scoping. This can expose sensitive documents that should be inaccessible to the requesting user, constituting an Insecure Direct Object Reference (IDOR) that aligns with the BOLA/IDOR checks in middleBrick’s 12 security checks.
Additionally, Firestore security rules may not fully restrict access when document IDs are derived from unchecked user input. If rules rely on wildcard matching or assume a fixed collection structure, a traversable document ID can bypass intended permissions. middleBrick’s unauthenticated scan can detect endpoints where a public or low-privilege context can reach documents across collection boundaries, flagging this as a high-severity finding under the BOLA/IDOR and Property Authorization checks. Because the scan tests the unauthenticated attack surface, it can identify whether an endpoint inadvertently exposes document traversal without requiring credentials.
In an Axum handler, the risk increases when route parameters are passed directly to Firestore read operations without normalization. For instance, using a raw user_id in a call to doc(&firestore, &format!("users/{}", user_id)) without validation allows logical traversal if the string contains path-like segments. MiddleBrick’s LLM/AI Security checks further highlight that public endpoints with AI integrations might leak system prompts or allow indirect data exfiltration when traversable paths expose sensitive documents. The scanner’s active prompt injection tests and output scanning for PII or API keys help identify whether a traversable path leads to data exposure or code execution risks in downstream AI-assisted workflows.
Because Firestore does not have a traditional filesystem, path traversal is realized through document hierarchy manipulation. An attacker might not read arbitrary files, but they can read documents across collections if the application does not enforce strict access controls and input validation. middleBrick’s inventory management and data exposure checks can surface these misconfigurations by comparing the intended access boundaries with runtime observations, providing prioritized findings with severity and remediation guidance to help developers tighten document access logic in Axum handlers.
Firestore-Specific Remediation in Axum — concrete code fixes
To prevent Path Traversal in an Axum application using Firestore, you must validate and sanitize all user inputs that map to Firestore document IDs or collection paths. Never directly concatenate user-controlled values into document references. Instead, enforce strict allowlists for document IDs and use Firestore’s built-in mechanisms to reference documents safely. The following examples demonstrate secure patterns.
First, validate the user_id parameter to ensure it contains only expected characters and does not include path-like segments. Use a regex or a simple check to reject strings containing /, .., or other traversal indicators. Then, construct the Firestore document reference using the validated ID only.
use axum::{routing::get, Router};
use google_cloud_firestore::client::Client;
use std::net::SocketAddr;
async fn get_user_profile(
Path(user_id): Path,
firestore: Extension,
) -> Result {
// Validate user_id: allow only alphanumeric, underscore, and hyphen
if !user_id.chars().all(|c| c.is_alphanumeric() || c == '_' || c == '-') {
return Err((StatusCode::BAD_REQUEST, "Invalid user_id".to_string()));
}
let doc_ref = firestore.doc(&format!("users/{}", user_id));
match doc_ref.get().await {
Ok(doc) => {
if doc.exists() {
let data: serde_json::Value = doc.get_data_as()?;
Ok(Json(data))
} else {
Err((StatusCode::NOT_FOUND, "User not found".to_string()))
}
}
Err(e) => Err((StatusCode::INTERNAL_SERVER_ERROR, e.to_string())),
}
}
#[tokio::main]
async fn main() {
let firestore = Client::new().await.expect("Failed to create Firestore client");
let app = Router::new()
.route("/users/:user_id/profile", get(get_user_profile))
.layer(Extension(firestore));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
This approach ensures that user_id cannot contain traversal sequences. The validation rejects any characters outside the allowlist, preventing logical access to other collections or documents. The document reference is then built from the sanitized ID, keeping access scoped to the intended user collection.
Second, if your data model requires nested collections, explicitly define the path structure and validate each segment. For example, if you have a path like organizations/{org_id}/projects/{project_id}, validate both org_id and project_id separately before referencing the Firestore document.
async fn get_project_data(
Path((org_id, project_id)): Path<(String, String)>,
firestore: Extension,
) -> Result {
if !org_id.chars().all(|c| c.is_alphanumeric() || c == '-') {
return Err((StatusCode::BAD_REQUEST, "Invalid org_id".to_string()));
}
if !project_id.chars().all(|c| c.is_alphanumeric() || c == '_') {
return Err((StatusCode::BAD_REQUEST, "Invalid project_id".to_string()));
}
let doc_ref = firestore.doc(&format!("organizations/{}/projects/{}", org_id, project_id));
// ... fetch and return data
}
By validating each segment, you maintain a clear hierarchy and avoid unintended cross-collection access. middleBrick’s CLI tool can be used to scan your endpoints and verify that such validation is consistently applied, producing JSON output that integrates with CI/CD pipelines via the GitHub Action to fail builds if risky patterns are detected.
Additionally, enforce Firestore security rules that mirror these access boundaries. Rules should not rely on client-supplied IDs alone; use request.auth to scope access. For example, ensure that a request can only read a user document if the document ID matches the authenticated user’s UID. middleBrick’s Pro plan supports continuous monitoring and provides compliance reports that can verify rule effectiveness against frameworks like OWASP API Top 10 and SOC2.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |