Http Request Smuggling in Actix with Bearer Tokens
Http Request Smuggling in Actix with Bearer Tokens — how this specific combination creates or exposes the vulnerability
HTTP Request Smuggling leverages discrepancies in how a frontend proxy/load balancer and the backend framework parse HTTP messages. In Actix web applications that rely on Bearer Tokens for authorization, smuggling can expose protected routes and enable unauthorized access when trust boundaries are misaligned.
Actix web uses a Rust-based HTTP parser (typically httparse or similar) to interpret incoming requests. When a reverse proxy normalizes or buffers requests before forwarding them to Actix, differences in handling headers such as Content-Length and Transfer-Encoding can cause one request to be split or merged. If an attacker can smuggle a request so that the proxy interprets one request and Actix interprets another, the smuggled request may bypass intended middleware checks, including Bearer Token validation.
Consider a setup where a proxy terminates TLS and forwards requests to Actix on the same host. If the proxy normalizes a request with both Content-Length: 0 and Transfer-Encoding: chunked by favoring one header, and Actix favors the other, the body boundary can shift. A request intended as a public health check might be interpreted by Actix as an authenticated admin call if the token arrives in a smuggled header or request line. Because Actix validates Bearer Tokens in middleware based on the request as it sees it, a smuggled request can slip through without the required token if the middleware layer is invoked on only one of the parsed requests.
Bearer Tokens amplify the impact: if the smuggled request targets a token-introspection or token-exchange endpoint, or an endpoint that uses token-based scopes, the attacker may gain access to protected data or perform actions under a legitimate user’s identity. For example, a GET /api/users/me that normally requires a valid Authorization header can be smuggled into a POST /api/users/me/change-role if header parsing diverges, and Actix processes the second request with the token attached to the first, unintentionally elevating privileges.
Real-world patterns include:
- CL.TE smuggling:
Content-Lengthstrictly defines the body;Transfer-Encodingis ignored by the proxy but honored by Actix, allowing a body to be interpreted as part of the next request. - TE.CL smuggling:
Transfer-Encodingis processed first by the proxy;Content-Lengthis ignored by the proxy but used by Actix, causing body misalignment. - Header smuggling: inserting headers like
X-Forwarded-HostorAuthorizationin the smuggled request to spoof authentication without a valid Bearer Token.
Because Actix does not inherently correct proxy-induced parsing mismatches, developers must ensure consistent interpretation of HTTP messages at the edge and validate tokens on every request path where authorization is required.
Bearer Tokens-Specific Remediation in Actix — concrete code fixes
To mitigate smuggling when using Bearer Tokens in Actix, align parsing behavior at the proxy and application layers, and enforce strict token validation on all routes that require authorization.
1) Normalize headers at the proxy
Ensure your reverse proxy (e.g., NGINX, Envoy) consistently handles Content-Length and Transfer-Encoding. Disable support for Transfer-Encoding if not needed, or ensure the proxy normalizes by removing ambiguous headers before forwarding. Example NGINX configuration to drop problematic headers:
server {
listen 443 ssl;
ssl_certificate /etc/ssl/certs/server.crt;
ssl_certificate_key /etc/ssl/private/server.key;
# Normalize incoming messages: prefer Content-Length, drop Transfer-Encoding
if ($http_transfer_encoding != "") {
return 400;
}
location /api/ {
proxy_pass http://actix_backend;
proxy_set_header Content-Length "";
proxy_set_header Transfer-Encoding "";
proxy_set_header Authorization $http_authorization;
}
}
This reduces the chance of Actix interpreting a different message than the proxy.
2) Validate Bearer Token on every handler or use a guarded guard
In Actix, implement an extractor that checks the Authorization header consistently and attach it to all protected routes. Do not rely on optional authentication that may be bypassed via smuggling.
use actix_web::{dev::ServiceRequest, Error, HttpRequest, web, guard, middleware, http::header};
use actix_web::http::Method;
use actix_web::error::ErrorUnauthorized;
use futures::future::{ok, Ready};
use std::task::{Context, Poll};
struct Authenticated;
impl actix_web::dev::Transform for Authenticated
where
S: actix_web::dev::Service,
S::Future: 'static,
{
type Response = actix_web::dev::ServiceResponse;
type Error = Error;
type InitError = ();
type Transform = AuthenticatedMiddleware<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ok(AuthenticatedMiddleware { service })
}
}
struct AuthenticatedMiddleware<S> {
service: S,
}
impl<S> actix_web::dev::Service<ServiceRequest> for AuthenticatedMiddleware<S>
where
S: actix_web::dev::Service<ServiceRequest, Response = actix_web::dev::ServiceResponse, Error = Error>,
S::Future: 'static,
{
type Response = actix_web::dev::ServiceResponse;
type Error = Error;
type Future = std::pin::Pin<Box<dyn std::future::Future<Output = Result<Self::Response, Self::Error>>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(cx)
}
fn call(&mut self, req: ServiceRequest) -> Self::Future {
// Enforce Bearer Token on every request
let auth_header = req.headers().get(header::AUTHORIZATION);
let token = match auth_header {
Some(value) => value.to_str().unwrap_or(""),
None => "",
};
if !token.starts_with("Bearer ") {
let response = actix_web::error::ErrorUnauthorized("Missing or invalid Bearer Token");
return Box::pin(async { Err(response) });
}
// Optionally validate token format/content here (e.g., JWT verification)
let fut = self.service.call(req);
Box::pin(async move {
let res = fut.await?;
Ok(res)
})
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
use actix_web::{web, App, HttpResponse, HttpServer};
HttpServer::new(|| {
App::new()
.wrap(Authenticated) // Apply globally so all routes require Bearer Token
.route("/api/public", web::get().to(|| async { HttpResponse::Ok().body("public") }))
.route("/api/secure", web::get().to(|| async { HttpResponse::Ok().body("secure") }))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
This extractor rejects requests missing or malforming the Bearer Token before routing, reducing the risk that a smuggled request is processed with an unintended authentication context.
3) Use consistent message parsing and body handling
Configure Actix to reject requests that contain both Content-Length and Transfer-Encoding, or ensure the body is parsed the same way at the proxy and application. Example Actix configuration to reject ambiguous requests:
use actix_web::{web, App, HttpServer, error::ErrorBadRequest};
async fn index(req: actix_web::HttpRequest) -> Result<actix_web::web::Json<serde_json::Value>, ErrorBadRequest<>> {
let headers = req.headers();
let has_cl = headers.contains_key("content-length");
let has_te = headers.contains_key("transfer-encoding");
if has_cl && has_te {
return Err(ErrorBadRequest("Ambiguous message encoding"));
}
Ok(actix_web::web::Json(serde_json::json!({ "ok": true })))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.route("/api/endpoint", web::post().to(index))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
By rejecting ambiguous encodings and enforcing a single parsing rule, you reduce the window for request splitting or merging that could bypass Bearer Token checks.
Frequently Asked Questions
How can I test if my Actix app is vulnerable to request smuggling with Bearer Tokens?
Content-Length and Transfer-Encoding headers through your proxy and observe whether Actix processes both as separate requests. Use a proxy-aware tool (e.g., curl with explicit headers) to verify whether a smuggled request bypasses Bearer Token validation.