logo

Database

Need

Neutralization of line breaks and control characters in request data written to logs

Context

• Usage of Rust 1.75+ for building application services

• Usage of the actix-web crate for handling HTTP requests

• Usage of the log crate for application logging

Description

1. Non compliant code

use actix_web::{post, web, HttpResponse};
use serde::Deserialize;

#[derive(Deserialize)]
struct LoginForm {
    username: String,
}
...

The `login` handler below records failed logins with `log::warn!`, inserting the submitted user name with the `{}` formatter. `{}` writes the value as it is, including line breaks. A user name such as `alice\n2026-09-23T10:00:00Z INFO login succeeded user=admin` produces a second, forged line that looks like a real event. Attackers use this to hide their activity behind fake entries, to break parsers so that alerts do not fire, or to frame another user. Values with ANSI escape sequences can also rewrite what an operator sees when reading logs in a terminal. The same problem applies to every request-derived value that reaches a log line: search terms, path segments, headers such as `User-Agent` or `X-Forwarded-For`, and file names.

2. Steps

• Escape line breaks and control characters in every request-derived value before passing it to `log` or `tracing` macros, for example with `str::escape_debug`.

• Truncate logged values to a reasonable maximum length.

• Use structured logging, such as `tracing` fields with a JSON formatter, so values are encoded separately from the message.

• Quote or delimit user-supplied values in log messages so their boundaries are visible.

• Keep secrets out of the log entirely; escaping prevents injection, not exposure.

3. Secure code example

use actix_web::{post, web, HttpResponse};
use serde::Deserialize;

const MAX_LOGGED_CHARS: usize = 64;

#[derive(Deserialize)]
struct LoginForm {
    username: String,...

The corrected handler passes the user name through `for_log` before logging it. The function truncates the value to 64 characters and applies `str::escape_debug`, which turns line breaks, tabs, quotes and every other control character into visible escape sequences such as `\n` or `\u{1b}`. The whole value therefore stays on a single log line, and a forged entry appears as literal text inside the original event instead of as a new record. Truncation also prevents attackers from filling the logs with very long values. Structured logging adds another layer: with `tracing` and a JSON formatter, the value is written as an escaped JSON string field, and log processors never confuse it with the message itself.

References

• 091. Log injection