Log Injection in Axum with Dynamodb
Log Injection in Axum with Dynamodb — how this specific combination creates or exposes the vulnerability
Log injection occurs when untrusted input is written directly into log entries without sanitization, enabling attackers to forge log lines, obscure real events, or trigger log-based attacks such as log poisoning or log injection-based SSRF. In an Axum application that uses DynamoDB as a persistence layer, this risk arises when request data—such as user IDs, query parameters, or DynamoDB key values—is reflected in application logs without validation or escaping.
Consider an Axum handler that fetches an item from DynamoDB using a user-supplied ID and logs the operation. If the ID contains newline characters or structured log delimiters (e.g., \n, {"event":), an attacker can inject additional log entries or metadata. For example, a crafted input like 123\n{"level":"ERROR"}Something is wrong can split one logical log line into multiple fabricated entries. Because DynamoDB stores the raw key values and the application may log these values verbatim, the combination of Axum’s request handling and DynamoDB’s storage semantics amplifies the exposure surface.
Moreover, DynamoDB attribute values that include control characters or structured text (e.g., JSON fragments) can distort log parsers that rely on line-oriented ingestion. When Axum logs the retrieved DynamoDB item for debugging, newlines or braces within string attributes can break log format assumptions, enabling injection of false context or even log forging that masks tampering. This is especially relevant when logs are forwarded to systems expecting newline-delimited events, as is common in many centralized log pipelines.
Real-world attack patterns mirror the OWASP API Top 10 logging and monitoring weaknesses and can align with findings from scans that flag insecure logging practices. While DynamoDB itself does not execute log injection, the way Axum processes and logs user-controlled data interacting with DynamoDB can create conditions where log integrity is compromised. The risk is not theoretical: malformed input reaching logs can obscure indicators of compromise, interfere with alerting, or be leveraged in multi-stage attacks where log trust is assumed.
Dynamodb-Specific Remediation in Axum — concrete code fixes
To mitigate log injection in Axum when working with DynamoDB, ensure that any data derived from user input or DynamoDB attributes is sanitized before inclusion in logs. This includes newlines, carriage returns, and structured characters that could alter log formatting. Below are concrete, idiomatic Axum examples that demonstrate safe handling.
1. Sanitize DynamoDB key values before logging
When logging keys or attributes retrieved from DynamoDB, replace or escape control characters. A utility function can normalize strings for log safety.
use axum::{routing::get, Router};
use aws_sdk_dynamodb::Client;
use std::sync::Arc;
fn sanitize_for_log(value: &str) -> String {
value.replace(['\n', '\r', '\t'], "_")
}
async fn get_item_handler(
Param(id): axum::extract::Path,
client: Arc<Client>,
) -> Result<impl axum::response::IntoResponse, (axum::http::StatusCode, String)> {
let response = client
.get_item()
.table_name("Items")
.set_key(Some(aws_sdk_dynamodb::types::AttributeValue::S(id.clone())))
.send()
.await
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
if let Some(item) = response.item() {
let safe_id = sanitize_for_log(&id);
tracing::info!(item_id = %safe_id, "fetched item from DynamoDB");
// further handling
}
Ok(axum::Json(response))
}
#[tokio::main]
async fn main() {
let client = Client::new(&aws_config::load_from_env().await);
let app = Router::new().route("/items/:id", get(get_item_handler)).with_state(Arc::new(client));
axum::Server::bind(&("0.0.0.0:3000").parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
2. Validate and log structured payloads safely
If you log DynamoDB item representations (e.g., for audit trails), avoid direct string interpolation. Instead, serialize to a controlled format and sanitize embedded strings.
use aws_sdk_dynamodb::types::AttributeValue;
use serde_json::json;
fn sanitize_attr(val: &AttributeValue) -> Option<String> {
match val {
AttributeValue::S(s) => Some(s.replace(['\n', '\r'], "_")),
AttributeValue::N(n) => Some(n.replace(['\n', '\r'], "_")),
_ => None,
}
}
fn build_safe_log_map(item: &std::collections::HashMap<String, AttributeValue>) -> serde_json::Value {
let mut map = serde_json::Map::new();
for (k, v) in item {
if let Some(safe) = sanitize_attr(v) {
map.insert(k.clone(), json!(safe));
}
}
json!(map)
}
// Example usage within handler after fetching item:
// let log_payload = build_safe_log_map(item);
// tracing::debug!(?log_payload, "DynamoDB item snapshot");
3. Enforce input validation at the framework boundary
Use Axum extractors and validators to reject inputs containing dangerous characters before they reach business logic or logging code.
use axum::async_trait;
use validator::Validate;
#[derive(Debug, Validate)]
struct ItemKey {
#[validate(length(min = 1, max = 255), regex(path = "RE_SAFE_ID"))]
id: String,
}
const RE_SAFE_ID: ®ex::Regex = regex::Regex::new(r\"^[\w-]+$\").unwrap();
async fn validated_handler(
Key(item_key): Key<ItemKey>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
// item_key.id is guaranteed safe for logging
tracing::info!(item_id = %item_key.id, "validated key used");
// proceed with DynamoDB operations
}
These patterns align with secure logging guidance and help ensure that DynamoDB-driven Axum applications avoid log injection while preserving useful diagnostic information.