Request Smuggling in Axum
How Request Smuggling Manifests in Axum
Request smuggling in Axum typically occurs when multiple HTTP parsers disagree on request boundaries. This happens when Axum's HTTP request parsing conflicts with a reverse proxy or load balancer sitting in front of it. The vulnerability arises from ambiguous Content-Length and Transfer-Encoding headers that can be interpreted differently by Axum versus upstream components.
In Axum applications, request smuggling often manifests through specific code patterns. Consider an Axum route handler that processes multipart form data:
use axum::extract::{Multipart, Form};
async fn upload(mut multipart: Multipart) -> String {
while let Some(field) = multipart.next_field().await.unwrap() {
let name = field.name().unwrap();
let data = field.bytes().await.unwrap();
// Process field data
}
"Upload complete".to_string()
}
let app = Router::new()
.route("/upload", post(upload));
The vulnerability occurs when Axum parses the multipart body differently than the upstream proxy. An attacker can craft requests with conflicting Content-Length and Transfer-Encoding headers, causing Axum to process a different request boundary than what the proxy expects. This can lead to request smuggling where one malicious request gets concatenated with a victim's legitimate request.
Another Axum-specific manifestation involves streaming responses. When using Axum's Response::stream or StreamBody, improper handling of chunked transfer encoding can create smuggling opportunities:
use axum::body::StreamBody;
use tokio::fs::File;
use tokio_util::io::ReaderStream;
async fn download() -> StreamBody {
let file = File::open("large_file.dat").await.unwrap();
StreamBody::new(file)
}
let app = Router::new()
.route("/download", get(download));
If the upstream proxy doesn't properly handle chunked encoding while Axum does, an attacker can manipulate chunk sizes to smuggle requests across the boundary.
Axum-Specific Detection
Detecting request smuggling in Axum requires understanding both the application code and the deployment infrastructure. Start by examining how Axum parses HTTP requests and how it handles different content encodings. The key is identifying where Axum's parser might disagree with upstream components.
Code analysis should focus on these Axum-specific patterns:
use axum::extract::RawBody;
async fn raw_handler(body: RawBody) -> String {
let bytes = body.bytes().await.unwrap();
// Raw body access without proper validation
String::from_utf8_lossy(&bytes).to_string()
}
Routes that accept RawBody or directly access the request stream are particularly vulnerable because they bypass Axum's built-in request validation.
For automated detection, middleBrick's black-box scanning approach is particularly effective for Axum applications. Since middleBrick tests the unauthenticated attack surface without requiring credentials or access to source code, it can identify request smuggling vulnerabilities by:
- Sending requests with conflicting Content-Length and Transfer-Encoding headers
- Testing various chunk size manipulations in chunked encoding
- Analyzing how the server responds to malformed HTTP requests
- Checking for discrepancies between expected and actual request processing
middleBrick's LLM/AI security module also helps detect if your Axum application has AI endpoints that might be vulnerable to prompt injection through smuggled requests. The scanner tests 27 regex patterns for system prompt leakage and performs active prompt injection testing across AI endpoints.
During scanning, middleBrick provides a security risk score (A–F) with specific findings about request smuggling vulnerabilities, including severity levels and remediation guidance. The scanner tests your Axum API in parallel across 12 security categories, ensuring comprehensive coverage of potential smuggling vectors.
Axum-Specific Remediation
Remediating request smuggling in Axum applications requires both code-level fixes and deployment configuration. Start with the application code by ensuring consistent request parsing and validation.
First, configure Axum to use strict HTTP parsing:
use axum::http::StatusCode;
use axum::response::IntoResponse;
async fn strict_handler() -> Result<impl IntoResponse, StatusCode> {
// Enforce strict content-length validation
Ok("Valid request")
}
let app = Router::new()
.route("/strict", post(strict_handler))
.with_state(AppState::new())
.http1_only(); // Disable HTTP/2 if not needed
The http1_only() configuration ensures Axum doesn't attempt to negotiate HTTP/2, which can introduce additional parsing ambiguities.
For multipart form handling, add explicit validation:
use axum::extract::{Multipart, TypedHeader};
use axum::http::header::CONTENT_TYPE;
async fn safe_upload(
TypedHeader(content_type): TypedHeader<CONTENT_TYPE>,
mut multipart: Multipart,
) -> String {
// Validate content type before processing
if !content_type.as_str().starts_with("multipart/form-data") {
return "Invalid content type".to_string();
}
while let Some(field) = multipart.next_field().await.unwrap() {
let name = field.name().unwrap();
let data = field.bytes().await.unwrap();
// Process with size limits and validation
}
"Upload complete".to_string()
}
Implement strict content-length validation middleware:
use axum::middleware::Next;
use axum::response::IntoResponse;
use axum::http::{Request, StatusCode};
async fn content_length_middleware(
mut req: Request,
next: Next,
) -> impl IntoResponse {
if let Some(len) = req.headers().get("content-length") {
if let Ok(len) = len.to_str().parse::() {
// Check against maximum allowed size
if len > 1_000_000 { // 1MB limit
return StatusCode::PAYLOAD_TOO_LARGE;
}
}
}
next.run(req).await
}
For deployment, ensure your reverse proxy and Axum application use consistent HTTP parsing. Configure your proxy to remove ambiguous headers before forwarding to Axum:
# Nginx configuration example
proxy_hide_header Transfer-Encoding;
proxy_set_header Content-Length "";
proxy_pass http://axum_backend;
Consider using middleBrick's continuous monitoring (Pro plan) to regularly scan your Axum API endpoints. This ensures new routes or changes don't reintroduce smuggling vulnerabilities. The scanner's 5–15 second scan time makes it practical to integrate into your development workflow.