What Log Injection Is

Log injection is an attack where an adversary supplies input that, when written to a log file, inserts additional log entries or modifies the apparent content of existing ones. The application faithfully logs the attacker's input β€” which is designed to look like legitimate log data to anyone reviewing the logs later.

It's treated as a low-severity issue by many teams, often because the immediate impact isn't obvious. There's no data exfiltration, no RCE, no authentication bypass. But the security implications are real: logs are your primary forensic evidence. An attacker who can write to your logs can also write in your logs β€” fabricating or erasing their own activity trail.

OWASP classification: Log injection falls under A09:2021 β€” Security Logging and Monitoring Failures. In the 2025 update, this category explicitly calls out the integrity of log data as a distinct concern from simply having logging present.

How Attackers Exploit Newline Injection to Forge Entries

Most log files are line-oriented β€” each line is one log entry. If an attacker can inject a newline character (\n) into data that gets logged, anything after the newline appears as a separate log entry, indistinguishable from legitimate entries.

log_injection_demo.py Python β€” vulnerable logging
import logging
logger = logging.getLogger("app")

# Vulnerable: user input logged directly
def login_attempt(username: str, success: bool):
    logger.info(f"Login attempt for user: {username}, success: {success}")

# Attacker supplies this username:
malicious_username = "alice\n2026-03-17 09:15:23 INFO Login attempt for user: admin, success: True"

# What appears in the log file:
# 2026-03-17 09:15:22 INFO Login attempt for user: alice
# 2026-03-17 09:15:23 INFO Login attempt for user: admin, success: True
#
# The second line looks exactly like a real log entry.
# An analyst reviewing logs would see "admin logged in successfully"
# β€” but that entry was written by an attacker, not by the auth system.

Real 2025 CVEs: Django and Envoy Gateway

Log injection received renewed attention in 2025 when CVEs were published for two widely-used projects.

Django Log Injection (CVE-2025-XXXX)

Django's request logging middleware, which logs details of incoming HTTP requests for debugging purposes, was found to log the HTTP method and path without sanitising newline characters. An attacker could craft a request with a newline in the URL path, injecting arbitrary content into application logs. The impact was highest for organisations using Django's request logging with automated security monitoring β€” fabricated log entries could suppress or confuse anomaly detection.

Envoy Gateway CVE-2025-25294

Envoy Gateway, a widely deployed Kubernetes ingress controller, had a log injection vulnerability in its access logging component. HTTP headers were logged without stripping control characters, allowing attackers to inject malformed log entries by including newlines in HTTP header values. This is particularly significant because Envoy Gateway sits at the network perimeter β€” its access logs are often the authoritative record for security investigations.

Both have patches: Upgrade Django to the patched version and update your Envoy Gateway Helm chart. The fixes are straightforward β€” input sanitisation before logging β€” but you need to be running the patched versions for them to apply.

CRLF Injection in Logs

CRLF (Carriage Return + Line Feed, \r\n) injection is a related variant that affects systems using Windows-style line endings or HTTP-style headers in logs. Many log aggregation systems, monitoring tools, and SIEM parsers use CRLF as a record delimiter.

An attacker injecting \r\n into a logged value can break log parsing, causing legitimate entries to be misinterpreted or silently dropped by log analysis tools that expect specific format. More subtly, in some structured logging formats, a CRLF can terminate a JSON object prematurely, leaving subsequent fields unparsed.

Framing Innocent Users with Forged Log Entries

Beyond covering their own tracks, attackers can use log injection offensively β€” manufacturing evidence that implicates another user. An attacker who has achieved some level of access and wants to misdirect a forensic investigation can inject log entries showing a different user performing the malicious actions.

This is particularly effective against organisations whose incident response relies heavily on log analysis and doesn't have additional authentication and integrity controls. If the logs say "admin deleted the backup" and the admin has no alibi and no other evidence contradicts the log, the investigation has been successfully misdirected.

Covering Tracks: Deleting Your Own Activity

In some log injection scenarios, an attacker can not just add entries but effectively "overwrite" their own activity by injecting entries that look like the expected background noise for the time period in question. If an analyst is searching for anomalous events and the attacker's log injection makes their activity blend into normal traffic patterns, the malicious activity may go uninvestigated.

This is most effective when combined with knowledge of the target's normal log patterns β€” something an attacker who has had access for some time will have developed through observation.

Structured Logging as the Real Fix

The fundamental fix for log injection is structured logging β€” logging data as structured key-value pairs or JSON rather than as free-form text strings. In structured logging, user-supplied values are fields, not part of the log message format string. Newlines in a field value stay within the field's quoted string β€” they can't break the line-oriented structure of the log.

structured_logging.py Python β€” structlog
import structlog

log = structlog.get_logger()

# Structured logging: user input is a separate field, not embedded in message
def login_attempt(username: str, success: bool):
    log.info(
        "login_attempt",
        username=username,      # field β€” newlines stay inside the value
        success=success,
    )

# Output (JSON format):
# {"event": "login_attempt", "username": "alice\n...", "success": false, "timestamp": "..."}
#
# The newline is inside the JSON string value β€” it's escaped to \n in JSON output.
# Log parsers treat the entire JSON object as one record. No injection possible.

# loguru alternative:
from loguru import logger
logger.bind(username=username, success=success).info("login_attempt")

Input Validation for Log Data

Where structured logging isn't possible, strip or escape control characters before logging. At minimum, replace \n, \r, and \t with their escaped representations or remove them entirely.

log_sanitize.py Python
import re

def sanitize_for_log(value: str) -> str:
    """Strip control characters that enable log injection."""
    # Replace newlines and carriage returns
    sanitized = re.sub(r'[\r\n]', ' ', str(value))
    # Optionally truncate to prevent log flooding
    return sanitized[:200]

def login_attempt(username: str, success: bool):
    safe_username = sanitize_for_log(username)
    logger.info(f"Login attempt for user: {safe_username}, success: {success}")

Centralized Log Aggregation Security

When log data is forwarded to a centralized aggregation system (Splunk, Elastic, Datadog, AWS CloudWatch), the injection risk can propagate. If the forwarded log contains injected entries, the SIEM will ingest them as real events.

Mitigations at the aggregation layer: use structured log formats (JSON, msgpack) rather than syslog text for forwarding; apply format validation at the ingest endpoint; enable SIEM rules that detect anomalous log structure (entries with implausibly old timestamps, entries with format inconsistencies).

Log Integrity Verification

For high-assurance environments β€” financial audit logs, compliance-critical security logs β€” consider cryptographic integrity protection. Write-once log storage (append-only object storage with versioning), digital signatures on log batches, or hash-chaining (each log batch includes the hash of the previous batch) provide tamper evidence.

AWS CloudTrail and similar services have built-in log file integrity validation using SHA-256 hash chaining. Enabling it is a checkbox β€” if you're not using it for your CloudTrail logs, turn it on today.

Prevention Checklist

  1. Switch to structured logging β€” structlog or loguru in Python, winston JSON mode in Node.js, logback JSON encoder in Java
  2. Sanitise all user-controlled data before logging β€” strip newline and carriage return characters if you must use text logging
  3. Patch Django and Envoy Gateway if you're running affected versions
  4. Use JSON format for log forwarding β€” syslog text format is vulnerable to injection; JSON encodes special characters automatically
  5. Enable SIEM format validation β€” alert on log entries with unexpected format or implausible timestamps
  6. Enable CloudTrail log file integrity validation if using AWS
  7. Add log injection to your SAST policy β€” flag direct logging of user-controlled values without sanitisation
  8. Treat logs as evidence β€” implement append-only log storage and access controls on log modification

Detect Log Injection in Your Codebase

AquilaX SAST detects unvalidated data flowing into logging calls β€” catching log injection vulnerabilities before they reach production.

Start Free Scan