Http Request Smuggling in Actix with Firestore
Http Request Smuggling in Actix with Firestore — how this specific combination creates or exposes the vulnerability
Http Request Smuggling occurs when an API processes HTTP requests differently in transit versus after normalization, allowing attackers to smuggle requests across security boundaries. When Actix serves as an API gateway or proxy in front of Firestore-backed services, the interaction between Actix request handling and Firestore’s strict resource naming can amplify risk if request parsing is inconsistent.
Actix Web is a Rust framework that routes requests based on patterns and headers. If Actix uses different parsing rules for front-end and back-end messages (e.g., differing handling of Content-Length or Transfer-Encoding), an attacker can craft a request that is interpreted as two separate requests by Actix and Firestore. A common pattern is CL.TE (Content-Length/Transfer-Encoding) smuggling: the attacker sends a request with both Content-Length and Transfer-Encoding headers. Actix might process the request body based on Content-Length, while the underlying Firestore client or proxy interprets Transfer-Encoding, causing the second request to be processed without authentication or with elevated privileges.
Because Firestore enforces resource-level security rules per document path, a smuggled request might target a document the attacker cannot normally access. For example, if Actix forwards a request to a Firestore endpoint like projects/myapp/databases/(default)/documents/users/attacker_target, the smuggled request could attempt to read or write data outside intended scope. Firestore’s authentication happens at the HTTP layer; if Actix does not normalize the request before forwarding, the Firestore backend might accept the request due to a valid session cookie or token that was smuggled into the second request.
Another variant involves batch writes or transactions. Firestore supports multi-operation batches; if Actix parses the batch boundary incorrectly due to header mismatches, an attacker can smuggle an additional operation into the batch. This can lead to Privilege Escalation or Data Exposure when the batch is executed under a higher-privileged service account. Because Firestore responses do not indicate routing anomalies, the attacker receives normal success codes while unauthorized operations occur.
Detection requires correlating raw HTTP messages with Firestore operation IDs and document paths. middleBrick scans for these class of issues under BOLA/IDOR and Unsafe Consumption checks, highlighting header inconsistencies and unexpected resource access patterns. Since Firestore logs include full resource paths and authentication context, you can trace whether a smuggled request reached the backend even when Actix appeared to reject it.
Firestore-Specific Remediation in Actix — concrete code fixes
Remediation focuses on normalizing HTTP messages before routing to Firestore and validating all headers and body boundaries consistently. Actix configurations should enforce strict header handling and avoid forwarding ambiguous requests.
1. Enforce a single message parsing mode
Disable support for both Content-Length and Transfer-Encoding in a way that prevents smuggling. Choose one body size mechanism and reject requests that contain both headers.
use actix_web::{web, App, HttpRequest, HttpServer, Responder};
use actix_web::http::header::{CONTENT_LENGTH, TRANSFER_ENCODING};
async fn validate_headers(req: HttpRequest) -> Result<(), actix_web::Error> {
let has_cl = req.headers().contains_key(CONTENT_LENGTH);
let has_te = req.headers().contains_key(TRANSFER_ENCODING);
if has_cl && has_te {
return Err(actix_web::error::ErrorBadRequest("Header conflict"));
}
Ok(())
}
async fn firestore_proxy(req: HttpRequest, body: web::Bytes) -> impl Responder {
// Validate before constructing Firestore request
validate_headers(req).await?;
// Forward to Firestore client...
"OK"
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.route("/api/data/{doc_id}", web::post().to(firestore_proxy))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
2. Normalize paths before Firestore operations
Ensure document paths are canonical and do not allow path traversal via smuggled parameters. Use Firestore’s official client with typed references.
use google_cloud_firestore::client::Client;
use google_cloud_firestore::document::Document;
use actix_web::{web, HttpResponse};
async fn get_document(
doc_id: web::Path<String>,
firestore_client: web::Data<Client>
) -> HttpResponse {
// Normalize doc_id: remove leading/trailing slashes, prevent ".." patterns
let normalized = doc_id.trim_matches('/');
if normalized.contains("..") || normalized.is_empty() {
return HttpResponse::BadRequest().body("Invalid document path");
}
let doc_ref = firestore_client.doc(&format!("projects/myapp/databases/(default)/documents/users/{}", normalized));
match doc_ref.get().await {
Ok(document) => HttpResponse::Ok().json(document),
Err(e) => HttpResponse::InternalServerError().body(format!("Firestore error: {:?}", e)),
}
}
3. Validate batch boundaries explicitly
If using Firestore batches, ensure the batch size is declared in a single, trusted source and not derived from request headers that can be smuggled.
use google_cloud_firestore::client::Client;
use google_cloud_firestore::write_batch::WriteBatch;
use actix_web::web;
async fn commit_batch(
updates: web::Json<Vec<(@str, serde_json::Value)>>,
firestore_client: web::Data<Client>
) -> Result<(), actix_web::Error> {
let mut batch = WriteBatch::new(firestore_client.clone());
for (path, data) in updates.iter() {
// Validate path format here
batch.update(path, data)?;
}
batch.commit().await?;
Ok(())
}
4. Use middleware to log and reject suspicious requests
Log raw headers and body sizes before routing. If a request exhibits header conflicts or unexpected encoding, reject it before it reaches Firestore.