Log Injection in Flask with Bearer Tokens
Log Injection in Flask with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Log injection occurs when untrusted input is written directly into application logs without validation, encoding, or separation. In Flask APIs that rely on Bearer token authentication, this typically happens when developer code includes raw request data—such as the incoming Authorization header value, token claims, or user-supplied parameters—into log lines. Because logs are often structured as plain text or simple key-value streams, a newline character (\n) or carriage return (\r) inside a token or parameter can allow an attacker to inject additional log entries. This can obscure the true sequence of events, impersonate other users, or hide follow-up actions like repeated authentication attempts.
Consider a naive Flask route that logs the token for debugging:
from flask import Flask, request
app = Flask(__name__)
@app.route("/profile")
def profile():
auth = request.headers.get("Authorization", "")
# Dangerous: directly embedding the Authorization header into logs
app.logger.info(f"Request profile for auth: {auth}")
return {"status": "ok"}
If an attacker sends Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVC00OiJ9\nX-Injection: injected, the log entry can split into two logical lines, making it appear as if a second request was processed. Worse, if the token itself contains malicious payloads (e.g., crafted JWTs with embedded newlines), attackers can hide tampering or cause log parsing tools to misinterpret ownership and scope. The risk is amplified in distributed environments where logs are aggregated; injected newlines or special characters can break structured formats like JSON or Common Event Format, complicating detection and forensic analysis.
Log injection with Bearer tokens intersects three dimensions—Log Injection, Bearer Tokens, and Flask—because the framework’s default behavior does not sanitize inputs before logging, and tokens often travel in headers that developers mistakenly treat as safe. Attack patterns relevant here include OWASP API Top 10 #10 (Security Logging and Monitoring Failures) and log forging techniques that can bypass audit trails. For example, an attacker might inject a fake preceding log line such as "INFO — User admin authenticated successfully" to create a false trail. Because the API’s authentication logic may rely on logs for anomaly detection (e.g., rate limiting or suspicious token reuse), obscured logs reduce the effectiveness of security controls. Remediation focuses on sanitization and structured output rather than trusting the format of incoming tokens or request metadata.
Bearer Tokens-Specific Remediation in Flask — concrete code fixes
To mitigate log injection when working with Bearer tokens in Flask, avoid directly interpolating raw header values into log messages. Instead, sanitize input by removing or escaping control characters and by using structured logging where feasible. Below are concrete, safe patterns for Flask applications.
1. Sanitize the Authorization header before logging
Strip newlines and carriage returns, and consider redacting the token or storing only a safe representation (e.g., token type and last 4 characters) in logs.
import re
from flask import Flask, request
app = Flask(__name__)
# Remove newlines and carriage returns to prevent log injection
def sanitize_log_value(value: str) -> str:
return re.sub(r'[\r\n]+', ' ', value.strip())
@app.route("/profile")
def profile():
auth = request.headers.get("Authorization", "")
safe_auth = sanitize_log_value(auth)
# Log a safe representation: redact most of the token
if auth.startswith("Bearer "):
token_suffix = auth[-4:] if len(auth) >= 4 else "****"
app.logger.info(f"Request profile for auth: Bearer ****{token_suffix} [sanitized={safe_auth[:20]}]")
else:
app.logger.warning(f"Missing or malformed Authorization header [sanitized={safe_auth[:20]}]")
return {"status": "ok"}
2. Use structured logging with explicit fields
Output logs in JSON to ensure line boundaries are preserved and parsers can handle embedded characters safely. This approach works well with log aggregation tools and reduces the risk of injection breaking log formats.
import json
from flask import Flask, request
app = Flask(__name__)
@app.route("/profile")
def profile():
auth = request.headers.get("Authorization", "")
log_entry = {
"event": "profile_request",
"auth_type": "Bearer" if auth.startswith("Bearer ") else "none",
"token_suffix": auth[-4:] if auth and len(auth) >= 4 else None,
"ip": request.remote_addr,
"user_agent": request.headers.get("User-Agent", ""),
}
app.logger.info(json.dumps(log_entry))
return {"status": "ok"}
3. Centralize logging configuration
Configure Flask’s logger to apply a filter that normalizes newlines across all handlers, ensuring consistent behavior even if other parts of the app log untrusted input.
from flask import Flask
import logging
app = Flask(__name__)
class NewlineFilter(logging.Filter):
def filter(self, record):
if hasattr(record, 'msg') and isinstance(record.msg, str):
record.msg = record.msg.replace('\n', ' ').replace('\r', ' ')
return True
app.logger.addFilter(NewlineFilter())
@app.route("/items")
def items():
auth = request.headers.get("Authorization", "")
app.logger.info(f"Items accessed with auth: {auth}")
return {"items": []}
These patterns ensure that Bearer token values do not corrupt logs or enable log injection. They align with secure coding practices by treating all HTTP input as untrusted, while still allowing useful audit trails. For teams using the middleBrick CLI (middlebrick scan <url>), continuous monitoring, or the GitHub Action to gate CI/CD pipelines, consistent logging hygiene reduces noise in security findings and supports more accurate detection of authentication anomalies.