Log Injection in Axum
How Log Injection Manifests in Axum
Log injection in Axum occurs when untrusted user input flows directly into log statements without proper sanitization. This manifests in several specific ways within Axum applications:
async fn handle_request(
Extension(logger): Extension,
Json(payload): Json<Request>,
) -> Result<impl IntoResponse> {
// Vulnerable: user-controlled data in log message
logger.info("Processing request from {} with data: {}",
payload.user_id, payload.data);
Ok("")
}
The critical vulnerability here is that payload.data can contain any characters, including newlines and control characters. An attacker could submit:
{
"user_id": "123",
"data": "normal data\nERROR: Critical system failure\nStack trace: ..."
}
This creates false log entries that could trigger alerts, hide malicious activity, or cause log parsers to fail. In Axum specifically, this often occurs in:
- Request logging middleware with user data in log messages
- Error handling that logs request bodies or query parameters
- Authentication middleware that logs user identifiers
- Database operation logging that includes user-supplied values
Axum's extractor-based design makes this particularly common because developers naturally want to log the extracted data:
async fn process_order(
Path(order_id): Path<String>,
Json(order): Json<Order>,
Extension(logger): Extension<Logger>,
) -> Result<impl IntoResponse> {
// Vulnerable: order_id from URL path is user-controlled
logger.info("Processing order {} for user {}", order_id, order.user_id);
// More dangerous: entire JSON body in logs
logger.info("Received order data: {}", serde_json::to_string(&order)?);
Ok("")
}
The second log statement is especially dangerous because it logs the entire request body, which could contain malicious content designed to break log parsers or inject false security events.
Axum-Specific Detection
Detecting log injection in Axum applications requires both static analysis and runtime monitoring. Static analysis tools can scan for patterns like:
// Pattern: logger.info!(... user-controlled data ...)
logger.info!("{}", user_input);
logger.warn!("Error processing {}: {}", id, user_data);
error!("Failed request: {}", serde_json::to_string(&request)?);
middleBrick's black-box scanning approach tests for log injection by sending payloads with newline characters and control sequences to API endpoints, then analyzing the response logs for injection artifacts. For Axum applications specifically, middleBrick:
- Identifies endpoints that log request data based on response patterns
- Sends payloads containing newline sequences and log injection patterns
- Analyzes response times and error messages for injection indicators
- Checks if log levels are improperly exposed in API responses
Runtime detection in production Axum applications should include:
use tracing_subscriber::fmt::format::FmtSpan;
#[tokio::main]
async fn main() {
// Configure structured logging with sanitization
let formatter = FmtSpan::ACTIVE
.with_target(false)
.with_level(true);
tracing_subscriber::fmt()
.with_max_level(Level::INFO)
.with_span_events(formatter)
.with_filter(tracing_subscriber::filter::EnvFilter::from_default_env())
.init();
// Start Axum app
let app = axum::Router::new()
.route(...);
axum::Server::bind(&([127, 0, 0, 1], 3000).into())
.serve(app.into_make_service())
.await
.unwrap();
}
For comprehensive security testing, middleBrick's continuous monitoring (Pro plan) can automatically scan your Axum APIs on a schedule, catching log injection vulnerabilities before they reach production. The GitHub Action integration allows you to fail builds when suspicious log patterns are detected during testing.
Axum-Specific Remediation
Remediating log injection in Axum requires a multi-layered approach. The most effective strategy combines input sanitization, structured logging, and careful log message construction:
use tracing::{info, warn, error};
use sanitizer::Sanitizer;
// Sanitize user input before logging
fn sanitize_for_log(input: &str) -> String {
input
.replace('\n', " ")
.replace('\r', " ")
.replace('\t', " ")
.chars()
.take(1000) // Limit length
.collect()
}
async fn secure_logging_example(
Json(payload): Json<Request>,
Extension(logger): Extension<Logger>,
) -> Result<impl IntoResponse> {
// Sanitize before logging
let safe_user_id = sanitize_for_log(&payload.user_id);
let safe_data = sanitize_for_log(&payload.data);
// Use structured logging instead of string interpolation
info!(
message = "Processing request",
user_id = %safe_user_id,
data_length = payload.data.len(),
data_preview = &safe_data[..std::cmp::min(100, safe_data.len())]
);
Ok("")
}
The structured logging approach above prevents injection by using key-value pairs instead of string formatting. The sanitize_for_log function removes dangerous characters and limits length.
For Axum applications using middleware, implement a logging middleware that sanitizes automatically:
use axum::{middleware::Next, Request, Response};
use http::{StatusCode, HeaderMap};
use tracing::{info, warn};
async fn logging_middleware(
req: Request,
next: Next,
) -> Result<Response, axum::BoxError> {
let start = std::time::Instant::now();
let method = req.method().clone();
let path = req.uri().path().to_string();
let headers = req.headers().clone();
let response = next.run(req).await;
let status = response.as_ref().map(|r| r.status()).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
let duration = start.elapsed();
// Sanitize headers before logging
let safe_headers: Vec<_> = headers
.into_iter()
.map(|(k, v)| (k.as_str(), v.to_str().unwrap_or("invalid").to_string()))
.collect();
info!(
message = "Request completed",
method = %method,
path = %path,
status = %status,
duration = %duration.as_millis(),
headers = ?safe_headers
);
Ok(response?)
}
middleBrick's Pro plan includes continuous monitoring that can alert you when log injection patterns are detected in production, allowing you to respond before attackers exploit vulnerabilities. The CLI tool (npm install -g middlebrick) can be integrated into your CI/CD pipeline to scan Axum applications before deployment.