Http Request Smuggling in Actix with Cockroachdb
Http Request Smuggling in Actix with Cockroachdb — how this specific combination creates or exposes the vulnerability
Http Request Smuggling arises when an HTTP request is interpreted differently by distinct components in a pipeline—incompatible parsing of message boundaries allows an attacker to smuggle a request from one victim’s intended routing context into another. In an Actix web application that uses Cockroachdb as the backend datastore, the risk is shaped by how Actix parses and routes messages and how application code builds database interactions.
Actix is a Rust framework that relies on typed, async handlers and typically expects a clear separation between incoming HTTP messages and downstream service calls. When developers compose Actix routes with custom middleware or chain multiple services (for example, a gateway and the Actix app), differences in how headers like Content-Length and Transfer-Encoding are handled can cause one request’s body to be consumed or left for the next request. Cockroachdb does not directly introduce parsing differences, but the way application code opens database connections and constructs queries can amplify the impact: if request body parsing is bypassed or misinterpreted, an attacker may smuggle an operation that alters transaction boundaries or leaks connection state information tied to Cockroachdb queries.
Consider a scenario where Actix routes accept both JSON commands and raw SQL-like strings that the application forwards to Cockroachdb. If Actix trusts a Content-Length header from a front-end proxy but the proxy normalizes messages differently than Actix’s underlying parser, a smuggled request may reach the handler with an unexpected body. Because the handler builds a Cockroachdb session and executes queries based on that body, the smuggled input can execute unintended SQL, leak prepared statement handles, or cause session-scoped data to be interpreted under a different tenant’s context. This is not a Cockroachdb flaw; it is a consequence of inconsistent message handling upstream of the database layer, where the Actix app fails to validate and normalize request structure before issuing database calls.
Real-world attack patterns relevant to this setup include classic TECL (Transfer-Encoding Chunked Length) smuggling, where a request with both Transfer-Encoding: chunked and Content-Length causes one server to process body bytes differently than the next. In an Actix + Cockroachdb deployment, such inconsistencies can lead to unauthorized database operations, exposure of query results belonging to other users, or bypass of intended transaction isolation. Because Cockroachdb often serves distributed transactions, a smuggled request might inadvertently participate in or interrupt a multi-statement transaction, increasing the severity of data exposure or integrity violations. MiddleBrick’s checks for BOLA/IDOR, Property Authorization, and Input Validation are effective at highlighting these routing and parsing gaps before they are weaponized.
Cockroachdb-Specific Remediation in Actix — concrete code fixes
Remediation focuses on strict request parsing, canonicalizing headers before routing, and ensuring database interactions are isolated per request with no reliance on implicit state carried over from malformed messages. Below are concrete Actix patterns and Cockroachdb code snippets that reduce the risk of request smuggling.
1. Enforce strict header parsing and reject ambiguous messages
Configure Actix to reject requests where Content-Length and Transfer-Encoding are both present. Use middleware to normalize and validate headers before they reach handlers.
use actix_web::{dev::ServiceRequest, Error, middleware::Next};
use actix_web::http::header::{self, TransferEncoding, ContentLength};
async fn validate_headers(req: ServiceRequest, next: Next<'_>) -> Result<actix_web::dev::ServiceResponse, Error> {
let has_te = req.headers().get(header::TRANSFER_ENCODING).is_some();
let has_cl = req.headers().get(header::CONTENT_LENGTH).is_some();
if has_te && has_cl {
return Err(actix_web::error::ErrorBadRequest("conflicting transfer encoding and content length"));
}
// Optionally remove Transfer-Encoding to prevent downstream misinterpretation
let req = if has_te { req.map_into_left_body() } else { req };
next.call(req).await
}
2. Use typed, deserialized commands instead of raw SQL forwarded to Cockroachdb
Avoid passing client-controlled strings directly to Cockroachdb. Use strongly-structed commands and prepared statements to ensure the query shape cannot be altered by smuggled input.
use cockroachdb_rs::Client;
use serde::Deserialize;
#[derive(Deserialize)]
struct CreateUser {
email: String,
name: String,
}
async fn create_user(
cmd: web::Json<CreateUser>,
db: web::Data<Client>,
) -> Result<impl Responder, Error> {
let cmd = cmd.into_inner();
// Use parameterized queries; never interpolate cmd.email into raw SQL
db.execute(
"INSERT INTO users (email, name) VALUES ($1, $2) RETURNING id",
&[&cmd.email, &cmd.name],
)
.await
.map(|_| HttpResponse::Created().finish())
.map_err(|e| error::ErrorInternalServerError(e.to_string()))
}
3. Isolate database sessions per request and avoid session reuse across parsed messages
Open a fresh Cockroachdb connection or use a transaction per Actix request, and do not allow connection state to leak across requests that may have been smuggled. This prevents a smuggled request from inheriting an unintended transaction.
use cockroachdb_rs::Connection;
async fn handle_request(req: HttpRequest, body: String) -> Result<HttpResponse, Error> {
// Create a dedicated connection for this request
let conn = Connection::connect("postgresql://...").await?;
// Begin a transaction scoped to this request only
let txn = conn.transaction().await?;
// Parse body strictly; abort if malformed
let parts: Vec<String> = body.lines().map(str::to_string).collect();
if parts.len() != 2 {
return Err(ErrorBadRequest("invalid input"));
}
txn.execute("INSERT INTO logs (entry) VALUES ($1)", &[&parts[0]]).await?;
txn.commit().await?;
Ok(HttpResponse::Ok().finish())
}
4. Normalize paths and reject malformed Host headers
Smuggling can also exploit path parsing differences. Ensure Actix uses a strict, canonical route matcher and that Cockroachdb-bound queries do not depend on path-derived identifiers that could be altered by a smuggled prefix.
use actix_web::web::Path;
async fn safe_query(
path: Path<(i32,)>, // enforces a single segment integer ID
db: web::Data<Client>,
) -> Result<HttpResponse, Error> {
let record_id = path.0;
let row = db.query_one("SELECT data FROM records WHERE id = $1", &[&record_id]).await?;
Ok(HttpResponse::Ok().json(row))
}
5. Apply middleware for consistent message length and body consumption
Add a lightweight Actix middleware layer that reads and fully consumes the request body according to a single interpretation (prefer Content-Length when TE is absent), discarding any leftover bytes that could be smuggled.
use actix_web::dev::{Payload, ServiceRequest, ServiceResponse};
use actix_web::Error;
use futures_util::future::{ok, Ready};
pub fn canonical_body_middleware() -> impl Fn(ServiceRequest) -> Ready<Result<ServiceRequest, Error>> {
|req: ServiceRequest| {
// Force body consumption via to_bytes(), ensuring no dangling fragments
let fut = req.into_body().to_bytes();
async move {
let body = fut.map_err(|e| actix_web::error::ErrorBadRequest(e.to_string()))?;
let req = ServiceRequest::from_parts(req.head().clone(), body.into());
Ok(req)
}
}
}