Request Smuggling in Actix
How Request Smuggling Manifests in Actix
Request smuggling is a critical vulnerability that arises when two HTTP parsers (typically a reverse proxy and a backend server) disagree on where one request ends and the next begins. This discrepancy allows attackers to smuggle a malicious request past the proxy and have it processed by the backend as a separate, unauthorized request. In Actix applications, this often occurs due to ambiguous HTTP headers like Content-Length and Transfer-Encoding being interpreted differently by the front-end proxy and Actix's HTTP parser (based on the hyper crate).
Actix strictly follows HTTP/1.1 standards: when both Content-Length and Transfer-Encoding: chunked headers are present, Transfer-Encoding takes precedence. However, many reverse proxies (e.g., nginx, Apache, cloud load balancers) may normalize headers differently—some strip Transfer-Encoding when Content-Length exists, others combine multiple Content-Length headers by summing values. If your proxy does not normalize consistently with Actix, an attacker can craft a request with conflicting headers that the proxy forwards intact, but which Actix parses as two separate requests. For example:
POST / HTTP/1.1
Host: target.com
Content-Length: 44
Transfer-Encoding: chunked
0
GET /admin HTTP/1.1
Host: target.com
If the proxy forwards all bytes (44) based on Content-Length, but Actix processes the chunked body and then treats the trailing GET /admin as a new request on the same connection, the attacker gains unauthorized access to /admin. This can lead to cache poisoning, session hijacking, or bypassing authentication (e.g., CVE-2020-10713 in similar Rust HTTP stacks). Actix does not perform header normalization itself—it expects a well-formed request from the proxy. Thus, the vulnerability lies in the deployment configuration, not in Actix's core code, but its strict compliance makes it susceptible to proxy discrepancies.
Actix-Specific Detection
Detecting request smuggling in an Actix deployment requires testing how the front-end proxy and Actix jointly handle ambiguous requests. Since middleBrick performs black-box scanning without credentials, it can simulate smuggling attacks by sending crafted requests and analyzing responses for signs of request splitting (e.g., receiving two responses from a single request, or unexpected status codes).
Manually, you can test by sending requests with both Content-Length and Transfer-Encoding headers using curl:
curl -v -X POST http://your-actix-app.com/ \
-H "Content-Length: 44" \
-H "Transfer-Encoding: chunked" \
--data-binary $'0
GET /admin HTTP/1.1
Host: your-actix-app.com
'If the response includes data from both the initial POST and the smuggled GET /admin (e.g., a 200 from /admin), the application is vulnerable. Similarly, test with multiple Content-Length headers:
curl -v -X POST http://your-actix-app.com/ \
-H "Content-Length: 4" \
-H "Content-Length: 20" \
--data-binary 'test/smuggled'If Actix processes the body as test/smuggled (20 bytes) instead of rejecting the request, the proxy likely combined the headers incorrectly. middleBrick automates these tests across all endpoints, checking for CL.TE, TE.CL, and multiple Content-Length variations. Its scanner reports a high-severity finding under the Input Validation category if it detects a discrepancy that could lead to smuggling, along with the specific probe that triggered it.
Actix-Specific Remediation
Remediating request smuggling in Actix deployments requires fixing the reverse proxy configuration first, as Actix itself does not normalize incoming headers. Ensure your proxy either rejects ambiguous requests or normalizes them to a single, unambiguous representation. For example, in nginx, add:
location / {
proxy_pass http://actix_backend;
# Remove Transfer-Encoding if Content-Length is present
proxy_set_header Content-Length $content_length;
proxy_set_header Transfer-Encoding "";
}This forces nginx to drop Transfer-Encoding when Content-Length exists, aligning with Actix's expectation (Actix will then use Content-Length). For Apache, use SetEnvIf to unset headers. Always test your proxy's behavior with tools like nginx -T or apachectl -t -D DUMP_RUN_CFG.
As defense-in-depth, add a middleware to your Actix application to reject requests with both headers. This catches any proxy misconfiguration before Actix processes the request. Here is a complete, production-ready middleware for Actix 4.x:
use actix_web::{
dev::{Service, ServiceRequest, ServiceResponse, Transform},
Error, HttpResponse,
};
use futures_util::future::{ready, LocalBoxFuture, Ready};
pub struct RequestSmugglingGuard;
impl Transform for RequestSmugglingGuard
where
S: Service, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse;
type Error = Error;
type Transform = RequestSmugglingGuardMiddleware;
type InitError = ();
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(RequestSmugglingGuardMiddleware { service }))
}
}
pub struct RequestSmugglingGuardMiddleware<S> {
service: S,
}
impl<S, B> Service<ServiceRequest> for RequestSmugglingGuardMiddleware<S>
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<B>;
type Error = Error;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
fn poll_ready(&self, ctx: &mut actix_web::dev::Context<'_>) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(ctx)
}
fn call(&self, req: ServiceRequest) -> Self::Future {
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 {
let response = HttpResponse::BadRequest()
.body("Request rejected: both Content-Length and Transfer-Encoding headers present");
return Box::pin(async move { Ok(req.into_response(response)) });
}
let fut = self.service.call(req);
Box::pin(async move {
match fut.await {
Ok(res) => Ok(res),
Err(e) => Err(e),
}
})
}
}
// Usage in your App:
// App::new().wrap(RequestSmugglingGuard).route("/", web::get().to(handler)); This middleware immediately rejects ambiguous requests with a 400 error, preventing them from reaching your handlers. Combine this with proxy normalization and continuous scanning using middleBrick's GitHub Action or CLI to ensure your Actix API remains secure against smuggling. Remember: middleBrick detects such issues by testing your live endpoint; it does not fix them, but provides actionable remediation steps like these.