Security

What is Log Injection (CWE-117)? Examples & Prevention | PenScan

Log injection happens when unsanitized input reaches your log files, letting attackers forge entries or break log parsers. See real code and fixes.

SP
Shreya Pillai July 28, 2026 6 min read Security
AI-friendly summary

What it is: Log injection (CWE-117) occurs when a product writes external, attacker-influenced input into a log file without neutralizing characters like CR/LF, letting the attacker forge or split log entries.

Why it matters: Forged log entries can hide an attacker's real activity, frame another user, or corrupt structured logs that downstream tooling (SIEM, log parsers) depends on.

How to fix it: Strip or encode CR/LF and any log-format delimiter characters from user-controlled values before they reach a log call, and prefer a logging framework with built-in encoding over raw string concatenation.

TL;DR: Log injection (CWE-117) lets an attacker plant fake entries in your application logs by putting newline characters in input that gets logged verbatim; fix it by encoding CR/LF (and format delimiters) at the point where untrusted data enters a log call, not by trusting the logging library alone.

At-a-Glance

Field Value
CWE ID CWE-117
OWASP Category A09:2025 - Security Logging and Alerting Failures
CAPEC CAPEC-93
Typical Severity Medium
Affected Technologies Java, Node.js, Python, PHP
Detection Difficulty Moderate
Last Updated 2026-07-28

What is Log Injection?

Log injection (CWE-117) occurs when a product constructs a log message from external input but doesn’t neutralize special characters — most importantly carriage-return/line-feed (\r\n) — before writing it to the log. As defined by MITRE under CWE-117 and classified by OWASP under A09:2025 - Security Logging and Alerting Failures, this lets an attacker terminate the current log line early and start a fabricated one that looks like a legitimate, independent entry.

Quick Summary

A login form that logs "Failed login for user: " + username looks harmless until an attacker submits a username containing a newline and a fake follow-up line — the log file now contains an entry that never really happened, e.g. a forged “admin login succeeded” line. This matters most for audit trails, fraud investigation, and any log feeding an automated alerting pipeline, since a convincing forged entry can mislead an investigator or suppress a real alert.

Jump to: At-a-Glance · What is Log Injection? · Quick Summary · Log Injection Overview · How Log Injection Works · Business Impact of Log Injection · Log Injection Attack Scenario · How to Detect Log Injection · How to Fix Log Injection · Framework-Specific Fixes for Log Injection · How to Ask AI to Check Your Code for Log Injection · Log Injection Best Practices Checklist · Log Injection FAQ · Vulnerabilities Related to Log Injection · References · Scan Your Own Site

Log Injection Overview

What: The application writes a log entry that includes unsanitized external input, and that input can contain characters (chiefly \r/\n) that break out of the intended single-line entry.

Why it matters: Logs are frequently the only forensic record of what happened during an incident — an attacker who can forge entries can misdirect an investigation, frame another account, or hide their own footprint among noise they planted.

Where it occurs: Any log call built by concatenating request data, headers, usernames, or other external values directly into the message string, in any language or logging framework.

Who is affected: Applications that log request-derived values (usernames, URLs, User-Agent headers, form fields) without passing them through an encoder first.

Who is NOT affected: Applications that only log fixed, developer-controlled strings and numeric/enum values with no free-text external input, or that use a structured logging library that encodes every field value by default.

How Log Injection Works

Root Cause

The log statement concatenates external input directly into the message text without removing or encoding \r/\n (and any delimiter meaningful to the log format), so a value containing those characters is interpreted by anything reading the log as a line/record boundary rather than as data.

Attack Flow

  1. The attacker finds an input point whose value is written into an application log (a username field, a URL path segment, a custom HTTP header).
  2. The attacker submits a value containing \r\n followed by text formatted to look like a separate, legitimate log entry (e.g. a fake “admin login succeeded” line).
  3. The application logs the value verbatim; the log file now contains two apparent entries — the real one and the attacker’s forged one — indistinguishable to a human reviewer or an automated log parser.

Prerequisites to Exploit

  • An input value that is both attacker-controlled and written into a log message.
  • No CR/LF (or format-delimiter) stripping/encoding between that input and the log call.
  • A log consumer (human reviewer, SIEM, log-parsing script) that treats each line as a discrete record.

Vulnerable Code

import java.util.logging.Logger;

public class LoginHandler {
    private static final Logger logger = Logger.getLogger(LoginHandler.class.getName());

    public void handleFailedLogin(String username) {
        logger.warning("Failed login attempt for user: " + username);
    }
}

This concatenates the raw username parameter straight into the log message. A username of bob%0d%0a2026-07-28 10:00:01 INFO Login succeeded for user: admin decodes to a real CR/LF, so the logged output contains what looks like a second, independent, successful admin login line.

Secure Code

import java.util.logging.Logger;

public class LoginHandler {
    private static final Logger logger = Logger.getLogger(LoginHandler.class.getName());

    private static String sanitizeForLog(String input) {
        if (input == null) return "";
        return input.replaceAll("[\r\n]", "_");
    }

    public void handleFailedLogin(String username) {
        logger.warning("Failed login attempt for user: " + sanitizeForLog(username));
    }
}

sanitizeForLog() replaces every CR/LF character with an inert placeholder before the value ever reaches the log call, so no attacker-controlled value can terminate the current line — the forged-entry technique no longer works regardless of what the rest of the string contains.

Business Impact of Log Injection

Integrity: Forged log entries corrupt the log’s value as a factual record — an investigator can no longer trust that a given line reflects something that actually happened.

  • Financial impact: A compromised audit trail can invalidate evidence needed for fraud recovery or insurance claims.
  • Compliance impact: Regulations that require tamper-evident audit logs (PCI-DSS, SOX-adjacent controls) treat a forgeable log as a control failure.
  • Reputation impact: If a forged entry is later found to have misled an incident response, it undermines confidence in the whole logging pipeline, not just the affected system.

Log Injection Attack Scenario

  1. A support portal logs every failed login as "Failed login for user: " + submittedUsername.
  2. An attacker submits a username crafted with an embedded CRLF and a fake line reading "...INFO Login succeeded for user: admin".
  3. Days later, during an incident review, an analyst greps the log for admin logins and finds the forged line, wasting investigation time chasing a login that never happened — or worse, missing the attacker’s real, differently-timed access nearby in the noise.

How to Detect Log Injection

Manual Testing

  • Submit a value containing %0d%0a (URL-encoded CRLF) followed by fabricated log-line text into every input that appears to be logged (usernames, search terms, custom headers).
  • Check whether the resulting log file contains two apparent entries instead of one.
  • Confirm whether the logging framework in use applies any default encoding, and whether that encoding is bypassed by the specific log call under test.

Automated Scanners (SAST/DAST)

SAST can flag log calls that concatenate a tainted variable directly into the message string; DAST needs to actually submit CRLF payloads and inspect server-side log output (not just the HTTP response), which most black-box scanners can’t reach without log access.

PenScan Detection

PenScan’s engines send CR/LF and log-delimiter payloads to reachable input points and, where log output is accessible, check for injected multi-line content.

False Positive Guidance

A log call that only ever receives a fixed enum value (e.g. a hardcoded status code) isn’t vulnerable even if it superficially resembles a logging pattern a scanner flags — confirm the logged value actually originates from external input before treating a finding as real.

How to Fix Log Injection

  • Strip or encode \r and \n from any external value before it reaches a log call.
  • Prefer a logging framework’s built-in parameterized/structured logging (e.g. passing values as separate arguments rather than string-concatenating them) since most modern frameworks encode those automatically.
  • For structured (JSON/CEF) logs, use the library’s real serializer rather than hand-building the string, so quote and delimiter characters are escaped correctly.

Framework-Specific Fixes for Log Injection

Java

Use SLF4J/Logback’s parameterized logging, which encodes arguments by default:

private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LoginHandler.class);
log.warn("Failed login attempt for user: {}", username);

Node.js

const sanitizeForLog = (input) => String(input).replace(/[\r\n]/g, '_');
logger.warn(`Failed login attempt for user: ${sanitizeForLog(username)}`);

Python/Django

import re

def sanitize_for_log(value):
    return re.sub(r'[\r\n]', '_', str(value))

logger.warning("Failed login attempt for user: %s", sanitize_for_log(username))

PHP

function sanitizeForLog(string $input): string {
    return str_replace(["\r", "\n"], '_', $input);
}

error_log("Failed login attempt for user: " . sanitizeForLog($username));

How to Ask AI to Check Your Code for Log Injection

Copy-paste prompt

Review the following code for CWE-117 (log injection): find every log call that concatenates external input into the message, and rewrite each one to either use the logging framework's parameterized/structured logging API, or explicitly strip CR/LF characters from the value first. Show the before and after for each call you change.

Log Injection Best Practices Checklist

  • Every log call that includes external input either uses parameterized logging or explicitly encodes CR/LF first.
  • Structured logging (JSON/CEF) uses the library’s real serializer, not hand-built string concatenation.
  • Log consumers (SIEM rules, review dashboards) don’t assume a line boundary always means a trustworthy record boundary.
  • Log-forging test payloads are included in the application’s security test suite.

Log Injection FAQ

What is log injection?

Log injection (CWE-117) happens when a product writes external input into a log file without neutralizing control characters, letting an attacker insert fake log entries or corrupt the log’s structure.

How is log injection different from log forging?

They describe the same weakness from two angles — “log injection” is the act of getting unsanitized input into the log, “log forging” is the outcome, where the injected content is crafted to look like a legitimate, different log entry.

Can log injection lead to code execution?

Directly, no — but if logs are later rendered in an admin dashboard without escaping, an injected payload can trigger stored XSS, and if logs feed a downstream parser or SIEM query, crafted newlines/delimiters can break or manipulate that processing.

What characters actually need to be neutralized?

At minimum CR (\r) and LF (\n), which let an attacker start a fake new log line; depending on the log format, delimiter characters like commas, pipes, or the format’s own escape character also need encoding.

Does this only affect plain-text log files?

No — structured logging (JSON, CEF) is safer by default but not immune; an attacker who can inject raw quotes or control characters into a field can still break the structure if the logging library doesn’t encode them.

How do I fix log injection without changing my logging library?

Strip or encode CR/LF (and any format-specific delimiter) from user-controlled values before they’re passed to the log call — most logging frameworks (Logback, Winston, Python’s logging module) apply this automatically once configured correctly, but raw string concatenation into a log message bypasses that.

How does PenScan detect log injection?

PenScan submits inputs containing CR/LF and log-delimiter sequences to known input points and checks whether server-accessible log or audit output reflects them unescaped.

CWE Name Relationship
CWE-116 Improper Encoding or Escaping of Output ChildOf
CWE-20 Improper Input Validation ChildOf

References

Scan Your Own Site

Manual code review catches what you know to look for. An automated scan catches what you didn’t. Scan your own website using PenScan to find log injection and other risks before an attacker does.