Header Injection in Axum
How Header Injection Manifests in Axum
Header injection vulnerabilities in Axum typically occur when user-controlled input is incorporated into HTTP response headers without proper validation. Unlike traditional web frameworks where header injection might involve CRLF sequences, Axum's type-safe approach means the primary vectors are different.
The most common manifestation in Axum applications is through dynamic header construction using user input. For example, when building custom authentication headers, CORS configurations, or error responses that include user-provided data. Consider this vulnerable pattern:
async fn handle_request(
Extension(db): Extension,
auth_header: Header<String>,
) -> impl IntoResponse {
let token = auth_header.0.strip_prefix("Bearer ").unwrap_or_default();
// Vulnerable: token directly used in header value
let user_id = db.validate_token(token).await.unwrap_or("unknown");
let response = Json(UserProfile { user_id });
// Header injection possible if token contains newlines
let mut headers = HeaderMap::new();
headers.insert("X-User-Id", HeaderValue::from_str(&user_id).unwrap());
(headers, response)
}
The vulnerability becomes critical when the user input contains newline characters ( or ), allowing attackers to inject additional headers or manipulate the response structure. Axum's type safety prevents classic HTTP response splitting via CRLF injection, but header injection remains possible through improper HeaderValue construction.
Another Axum-specific pattern involves dynamic CORS header generation:
async fn cors_handler(
Origin(origin): Origin,
) -> impl IntoResponse {
// Vulnerable: origin directly used without validation
let mut headers = HeaderMap::new();
headers.insert("Access-Control-Allow-Origin",
HeaderValue::from_str(origin).unwrap());
// Additional headers can be injected
headers.insert("X-Custom-Header",
HeaderValue::from_str("<script>alert(1)</script>").unwrap());
(headers, Json(Status::Ok))
}
Header injection in Axum also commonly occurs through error handling middleware that reflects user input in response headers. When exceptions occur, some applications include diagnostic information in custom headers without proper sanitization.
Axum-Specific Detection
Detecting header injection in Axum applications requires understanding both the framework's patterns and the specific attack vectors. Static analysis tools often miss Axum-specific vulnerabilities because they don't understand the framework's type system and middleware patterns.
Runtime detection with middleBrick is particularly effective for Axum applications. The scanner tests for header injection by attempting to inject newline characters and special sequences into various header inputs. Here's what middleBrick specifically looks for in Axum APIs:
middlebrick scan https://api.example.com/auth
The scanner tests common injection patterns including:
- Newline character injection ( , ) in authorization headers
- Header splitting attempts in custom header values
- Cross-origin header manipulation attempts
- Response splitting via malformed header sequences
For Axum specifically, middleBrick analyzes the framework's middleware chain to identify potential injection points. The scanner examines how headers are constructed throughout the request lifecycle, from initial parsing through middleware processing to final response generation.
Manual testing for header injection in Axum should include:
curl -H "Authorization: Bearer validtoken
X-Injected-Header: malicious-value" \
https://api.example.com/protected
Watch for unexpected headers appearing in the response or changes in response behavior. Axum's strong typing means classic CRLF injection won't work, but improper HeaderValue construction can still lead to injection vulnerabilities.
middleBrick's LLM/AI security module also checks for header injection in AI-specific endpoints, testing for prompt injection through header manipulation in LLM APIs built with Axum.
Axum-Specific Remediation
Remediating header injection in Axum requires leveraging the framework's type safety while implementing proper input validation. The key is ensuring all user-controlled input is properly sanitized before being used in header construction.
The most effective approach uses Axum's HeaderValue::from_str with validation:
use axum::http::HeaderValue;
use regex::Regex;
const HEADER_REGEX: &'static str = r"^[\w\d\.\-]+$";
fn validate_header_value(input: &str) -> Result<HeaderValue, &'static str> {
if input.contains('\n') || input.contains('\r') {
return Err("Header contains newline characters");
}
// Validate against allowed pattern
if !Regex::new(HEADER_REGEX).unwrap().is_match(input) {
return Err("Header contains invalid characters");
}
HeaderValue::from_str(input).map_err(|_| "Invalid header value")
}
async fn secure_handler(
auth_header: Header<String>,
) -> impl IntoResponse {
let token = auth_header.0.strip_prefix("Bearer ").unwrap_or_default();
// Validate token before using in headers
let user_id = validate_header_value(&token)
.unwrap_or_else(|_| HeaderValue::from_static("unknown"));
let mut headers = HeaderMap::new();
headers.insert("X-User-Id", user_id);
(headers, Json(Status::Ok))
}
For CORS handling in Axum, implement a whitelist approach:
async fn cors_handler(
Origin(origin): Origin,
) -> impl IntoResponse {
let allowed_origins = ["https://example.com", "https://app.example.com"];
let origin_str = origin.as_str();
let is_allowed = allowed_origins.contains(&origin_str);
let mut headers = HeaderMap::new();
if is_allowed {
headers.insert("Access-Control-Allow-Origin",
HeaderValue::from_str(origin_str).unwrap());
}
(headers, Json(Status::Ok))
}
Axum's middleware system provides excellent opportunities for centralized header validation:
use axum::middleware::Next;
use axum::http::{Request, Response};
async fn header_validation_middleware(
mut req: Request<Body>,
next: Next<Body>,
) -> Result<Response<Body>> {
// Validate headers before processing
for (name, value) in req.headers().iter() {
if let Ok(value_str) = value.to_str() {
if value_str.contains('\n') || value_str.contains('\r') {
return Ok(Response::builder()
.status(400)
.body(Body::from("Invalid header"))
.unwrap());
}
}
}
next.run(req).await
}
let app = Router::new()
.route("/api/secure", get(secure_handler))
.layer(header_validation_middleware);
Using Axum's type system effectively prevents many injection vectors. The Header<T> extractor ensures type safety, while proper validation of HeaderValue construction prevents injection through malformed inputs.