Crlf Injection in Axum (Rust)
Crlf Injection in Axum with Rust — how this specific combination creates or exposes the vulnerability
Crlf Injection occurs when an attacker can inject carriage return (CR, \r) and line feed (\n) characters into HTTP headers or the response body, causing header injection or response splitting. In Axum with Rust, this typically arises when user-controlled input is reflected into headers such as Location, Content-Type, or custom headers without proper sanitization. Axum’s extractor and response builder APIs allow developers to construct responses dynamically; if user data is passed directly into header values, an attacker can supply \r\n sequences to inject additional headers or break out of the message body into a new response.
For example, an endpoint that redirects based on a user-supplied `next` parameter can be abused if the parameter contains \r\n. An input like https://example.com\r\nSet-Cookie: malicious=1 can lead to header injection, causing the client to receive an unintended Set-Cookie header. Axum’s middleware and routing layer do not inherently sanitize inputs for header context; the responsibility falls on the developer to validate and encode any user-controlled data before it reaches the response builder. Because Axum is built on Hyper and relies on safe Rust abstractions, the language’s type system does not prevent logical errors where malformed data is passed to header setters, making application-layer validation essential.
This vulnerability maps to the OWASP API Top 10 category '2023-A1: Broken Object Level Authorization' when combined with BOLA/IDOR patterns, and it can also facilitate HTTP response splitting, which is relevant to the 'Data Exposure' and 'Input Validation' checks performed by middleBrick. The scanner tests whether injected CRLF sequences result in unexpected headers or body splits, reporting findings with severity and remediation guidance. Properly validating and encoding newlines in user input mitigates the risk and ensures compliance with secure coding practices.
Rust-Specific Remediation in Axum — concrete code fixes
To prevent Crlf Injection in Axum, you must sanitize user-controlled data before using it in headers or the response body. The safest approach is to reject or encode CR and LF characters in any input that may become a header value or part of the status message. Below are concrete Rust examples demonstrating secure handling within an Axum handler.
First, define a helper to reject disallowed characters for header-safe strings:
/// Rejects characters that could lead to header or response splitting.
fn is_header_safe(value: &str) -> bool {
!value.contains('\r') && !value.contains('\n')
}
/// Validates a redirect target to prevent CRLF injection.
fn validate_redirect_target(url: &str) -> Result<(), String> {
if !is_header_safe(url) {
return Err("Invalid characters in redirect target".to_string());
}
// Optional: enforce a strict allowlist or URL validation
if url.starts_with("http://") || url.starts_with("https://") {
Ok(())
} else {
Err("Redirect target must be an absolute HTTP(S) URL".to_string())
}
}
Use this validator in an Axum handler that performs redirects:
use axum::{
extract::Query,
response::{IntoResponse, Redirect},
};
use std::net::SocketAddr;
use axum::routing::get;
use axum::Router;
async fn redirect_handler(
Query(params): Query<std::collections::HashMap<String, String>>
) -> Result<impl IntoResponse, (axum::http::StatusCode, String)> {
let target = params.get("next").ok_or_else(|| {
(axum::http::StatusCode::BAD_REQUEST, "Missing 'next' parameter".to_string())
})?;
validate_redirect_target(target)?;
Ok(Redirect::found(target))
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/redirect", get(redirect_handler));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
For custom headers, avoid direct concatenation with user input. Instead, use Axum’s typed header types or explicitly encode newlines:
use axum::{
async_trait,
extract::Extension,
headers::{HeaderMap, HeaderValue, InvalidHeaderValue},
response::Response,
};
use std::convert::TryFrom;
fn set_safe_header(mut response: Response, value: &str) -> Result<Response, InvalidHeaderValue> {
let sanitized: String = value.chars().filter(|&c| c != '\r' && c != '\n').collect();
let header_value = HeaderValue::from_str(&sanitized)?;
response.headers_mut().insert("X-Custom-Reason", header_value);
Ok(response)
}
These patterns ensure that CR and LF characters cannot alter the structure of HTTP messages, effectively mitigating Crlf Injection in Axum services. When combined with middleBrick’s checks for Input Validation and Data Exposure, you can verify that your implementation correctly handles edge cases and adheres to secure coding standards.