Format String in Axum with Mutual Tls
Format String in Axum with Mutual Tls — how this specific combination creates or exposes the vulnerability
A format string vulnerability occurs when user-controlled input is passed directly into a formatting function such as format! or println! without explicit format specifiers. In Axum, this typically arises in request handlers that construct log lines, error messages, or HTTP responses using unchecked user input. When combined with Mutual Tls (mTLS), the risk profile changes in two ways.
First, mAxum applications using mTLS often terminate TLS at the router or load balancer and forward requests internally over plaintext HTTP. If the handler then incorporates data from the mTLS-authenticated client (such as a certificate common name, serial number, or an identity claim) into a logging or formatting call, an attacker who can present a valid client certificate may supply malicious format sequences. Because mTLS ensures the client is authenticated, the developer may assume the data is safe and skip validation, inadvertently creating a trusted-channel scenario where format string exploitation becomes viable.
Second, mTLS metadata is commonly used for authorization and logging. For example, a handler might read the peer certificate subject and embed it directly into a response or a structured log using format!("User: {subject}"). If the certificate subject contains format specifiers (e.g., a crafted Common Name like %s %s %s), the format call can read or write arbitrary memory when the logging or formatting library processes the string. This turns a normally secure mTLS identity binding into an injection vector. The vulnerability is not in mTLS itself but in the unchecked use of identity-derived data in format operations, which can lead to information disclosure or code execution when logs or outputs are rendered.
Real-world patterns include using the tower-rs middleware stack with custom authorization logic that passes certificate fields into format strings, or Axum extractors that bind identity claims into error messages. Because mTLS provides strong authentication, developers may relax input validation, inadvertently enabling these trusted-input paths to become high-impact attack vectors aligned with the OWASP API Top 10 category on Injection.
Mutual Tls-Specific Remediation in Axum — concrete code fixes
Remediation focuses on strict validation and safe formatting of any data derived from the mTLS context. Never directly interpolate certificate fields, serial numbers, or identity claims into format strings. Use explicit formatting specifiers and treat mTLS metadata as untrusted input.
Example 1: Safe handling of certificate subject in Axum with mTLS metadata passed via extensions.
use axum::{routing::get, Router, extract::RequestPartsExt, http::request::Parts};
use std::sync::Arc;
async fn handler(parts: RequestParts<Arc<()>>) -> String {
// mTLS identity is stored in request extensions after TLS termination
let subject = parts.extensions().get::<String>()
.map(|s| s.as_str())
.unwrap_or("unknown");
// Safe: positional formatting with explicit specifier, no user format chars
format!("Authenticated subject: {}", subject.replace("%", "%25"))
}
fn app() -> Router {
Router::new().route("/who", get(handler))
}
Example 2: Using the format_args! macro when constructing log records to avoid accidental formatting exploits, and sanitizing input with a replace for percent signs.
use log::info;
fn log_identity(cert_subject: &str) {
// Sanitize percent signs to prevent format string exploitation
let safe_subject = cert_subject.replace("%", "%25");
info!(target: "mtls_auth", "Certificate subject: {}", safe_subject);
}
Example 3: In a middleware context, validate and extract mTLS fields before use, avoiding direct formatting of untrusted data.
use axum::{middleware::Next, extract::Request, http::Response};
use std::future::Future;
async fn mtls_sanitizer(
mut request: Request,
next: Next<impl FnOnce(Request) -> Response<impl Future<Output = ()>>>,
) -> Response<impl Future<Output = ()>> {
if let Some(cert_subject) = request.extensions().get::<String>() {
// Validate: allow only alphanumeric, spaces, and common punctuation
let validated = cert_subject.chars()
.filter(|c| c.is_alphanumeric() || " .-_".contains(*c))
.collect::<String>();
request.extensions_mut().insert(validated);
}
next.run(request).await
}
These examples demonstrate that mTLS-specific remediation in Axum centers on treating authenticated identity data as potentially malicious, applying consistent sanitization (especially percent signs), and using explicit formatting rather than dynamic format strings.