Request Smuggling in Actix with Firestore
Request Smuggling in Actix with Firestore — how this specific combination creates or exposes the vulnerability
Request smuggling occurs when an API processes HTTP requests differently in transit versus after normalization, allowing attackers to smuggle requests between fronting services (like load balancers) and the Actix application. When Actix services interact with Google Cloud Firestore, the combination of HTTP parsing inconsistencies and Firestore’s strict gRPC/HTTP semantics can expose smuggling risks.
Actix-web is a Rust framework that handles requests through pipelines and extractors. If Actix is fronted by a reverse proxy or API gateway that terminates TLS and forwards requests to Actix, mismatches in how headers like Content-Length and Transfer-Encoding are handled can cause two distinct requests to be merged or split. For example, a request with both Transfer-Encoding: chunked and Content-Length may be interpreted differently by the proxy and by Actix, enabling an attacker to smuggle a second request that bypasses size limits or authentication checks.
Firestore introduces additional complexity. Firestore client libraries typically use gRPC over HTTP/2 or REST over HTTPS, and Firestore operations require precise header handling (e.g., Authorization: Bearer, Content-Type: application/json) and exact request bodies. If Actix forwards or proxies requests to Firestore without canonicalizing the HTTP method, path, or headers, a smuggled request may reach Firestore with an unintended method or path. This can lead to unauthorized reads or writes if the smuggled request targets a different Firestore document path or uses a different authentication context.
Consider an Actix endpoint that accepts a Firestore document path from user input and proxies the request to Firestore. If the endpoint does not strictly validate and normalize the path, an attacker can craft a request with an encoded path segment that appears valid to the proxy but resolves to a different document once processed by Actix. A smuggling payload might use a request like:
POST /v1/projects/myapp/databases/(default)/documents/items HTTP/1.1
Host: firestore.googleapis.com
Content-Length: 5
X-Forwarded-For: 10.20.30.40
{"f":
POST /v1/projects/myapp/databases/(default)/documents/secret HTTP/1.1
Host: firestore.googleapis.com
Content-Length: 0
}
If the proxy interprets the first request’s body as 5 bytes and Actix parses the second request independently, the second request may execute with the /documents/secret path, bypassing intended access controls. Firestore logs may show the operation under the wrong identity if the second request carries a forged token or lacks proper validation, leading to data exposure or unauthorized mutation.
Because middleBrick scans the unauthenticated attack surface and includes checks such as Input Validation and Property Authorization, it can surface indicators that suggest inconsistent handling of request parsing when Firestore endpoints are involved. While middleBrick detects and reports these patterns, remediation requires fixing how Actix and its upstream services parse and route requests.
Firestore-Specific Remediation in Actix — concrete code fixes
To mitigate request smuggling when Actix interacts with Firestore, enforce strict request canonicalization, validate and normalize paths, and avoid forwarding raw user input as Firestore resource names. Below are concrete patterns you can apply in Actix to reduce risk.
1. Strict header and body parsing
Ensure Actix rejects requests that contain both Content-Length and Transfer-Encoding. Use middleware or a custom guard to drop ambiguous requests before they reach business logic.
use actix_web::{web, App, HttpServer, HttpRequest, Error};
use actix_web::http::header::{HeaderValue, CONTENT_LENGTH, TRANSFER_ENCODING};
async fn validate_headers(req: HttpRequest) -> Result<(), Error> {
let has_content_length = req.headers().contains_key(CONTENT_LENGTH);
let has_transfer_encoding = req.headers().contains_key(TRANSFER_ENCODING);
if has_content_length && has_transfer_encoding {
return Err(actix_web::error::ErrorBadRequest("Invalid headers"));
}
Ok(())
}
// In your route configuration:
// .route("/api/{tail:.*}", web::post().to(handler).guard(validate_headers))
2. Canonicalize Firestore document paths
Never directly concatenate user input into Firestore document paths. Normalize paths by resolving . and .. segments and enforcing a strict prefix.
use firestore_rs::FirestoreDb;
use url::Url;
fn canonical_firestore_path(base: &str, user_segment: &str) -> Result {
let base_url = Url::parse(base).map_err(|_| "invalid base")?;
let resolved = base_url.join(&urlencoding::encode(user_segment).as_ref())
.map_err(|_| "join failed")?;
let path = resolved.path().trim_end_matches('/').to_string();
// Ensure the path stays within the allowed collection
if path.startsswith(&format!("{}/", base)) {
Ok(path)
} else {
Err("path traversal detected".into())
}
}
// Example usage in Actix handler:
// let doc_path = canonical_firestore_path(
// "projects/myapp/databases/(default)/documents/items",
// &item_id
// )?;
3. Use Firestore client libraries safely
Prefer the official Firestore client for Rust where available, or construct requests with explicit parameters. Avoid dynamically building Firestore REST URLs from user input. When using the Firestore REST API, explicitly set the parent and document name fields in the request body instead of embedding them in the URL path.
use serde_json::json;
let doc_body = json!({
"fields": {
"name": { "stringValue": "projects/myapp/databases/(default)/documents/items/safe-id" }
},
"ignoreUnknownFields": true
});
// Firestore REST example with explicit parent in body, not URL
// POST https://firestore.googleapis.com/v1/projects/myapp/databases/(default)/documents:commit
// Body includes the writes array with full resource names
4. Enforce origin and referer checks for sensitive routes
Add checks that the request’s origin or referer aligns with your service when accessing Firestore-related endpoints. This complements path validation and helps detect smuggling attempts that rely on missing referrer context.
async fn validate_origin(req: HttpRequest) -> Result<(), Error> {
if let Some(origin) = req.headers().get("Origin") {
if origin != "https://your-trusted-domain.com" {
return Err(actix_web::error::ErrorForbidden("origin mismatch"));
}
}
Ok(())
}
5. Apply rate limiting and logging
Use Actix middleware to apply per-client rate limits and log request metadata. This reduces the impact of smuggling attempts that rely on high request volume to confuse logging or monitoring systems.
// Example using actix-web-rate-limit
// let app = App::new()
// .wrap(RateLimiter::new(|req: &ServiceRequest| {
// req.peer_addr().map(|a| a.ip()).unwrap_or_else(|| "unknown".parse().unwrap())
// }).with_rate(Rate::per_second(10)))
// .service(your_route);