Skip to content
Data protection Reviewed 2026-09-12

Sensitive logging and log injection

Logs often have more readers and a longer retention period than the originating request. Copying a password, access token, session cookie, or private payload into a log creates another place where it can be exposed. Separately, untrusted newlines and delimiters can make a plain-text event appear to contain additional records.

Trust boundary: request data must not choose log structure, and access to operational logs must not grant access to authentication secrets. OWASP's logging guidance recommends selecting necessary event data, excluding sensitive values, and protecting logs throughout their lifecycle.

Unsafe example

logger.info(f"Login user={request_user} token={access_token}")

The token is disclosed to the logging system, and line breaks in a request-supplied value can disrupt a line-oriented collector. Replacing interpolation with %s alone would not remove either problem.

Safer example: a small event contract

This Python helper records only a server-generated correlation ID and a fixed outcome. It deliberately has no token, password, cookie, or arbitrary payload parameter.

import json
import re

def login_event(request_id: str, outcome: str) -> str:
    if not re.fullmatch(r"[A-Za-z0-9_-]{1,64}", request_id):
        raise ValueError("Invalid server request ID")
    if outcome not in {"success", "denied", "error"}:
        raise ValueError("Invalid outcome")
    return json.dumps(
        {"event": "login", "request_id": request_id, "outcome": outcome},
        ensure_ascii=True,
        separators=(",", ":"),
    )

# request_id is created by trusted server middleware.
logger.info("%s", login_event(request_id, "denied"))

Allowlisting the event shape prevents accidental copying of a whole request. JSON preserves field boundaries when the collector parses it as JSON. The example also restricts the identifier alphabet, so a client cannot inject raw line breaks through that field. Use a logging library's native structured-event support where available, and confirm that every downstream collector and viewer preserves the structure.

Some investigations need account or tenant identifiers. Add only approved fields, with their source, access controls, and retention documented. Pseudonymous identifiers can still be sensitive and linkable. Sanitize separately for the final display context if logs are rendered in a web interface; JSON encoding is not HTML escaping.

Configure reverse proxies, APM, error reporting, and SDKs too: application code may be clean while a request-body recorder still captures secrets. Restrict log access, protect integrity, set retention, and test redaction. If a credential has already been logged, remove exposure where feasible and rotate or revoke it; changing future logging does not invalidate the leaked credential.

Regression test

Assert the helper emits one parseable JSON object with exactly the three approved keys. Reject a request ID containing a newline and an unsupported outcome. In an integration fixture, send a fictional token marker through the authentication flow and assert it is absent from application, proxy, APM, and error logs. Do not use a real secret as a test marker.

Related: hardcoded secrets, information disclosure, and format string injection.

References: CWE-532 — insertion of sensitive information into a log file and CWE-117 — improper output neutralization for logs.