Logging Security Guidelines
This document outlines secure logging practices to prevent CWE-117 log injection vulnerabilities and comply with OWASP logging guidelines.
Security Principles
1. Never Log Sensitive Data
NEVER log the following types of data:
- Authentication tokens (JWT, Bearer tokens, API keys)
- Passwords or password hashes
- Session identifiers
- Payment information (card numbers, CVV, bank details)
- Personal Identifiable Information (PII)
- Raw request/response payloads without sanitization
- Internal system secrets
2. Sanitize All User Input Before Logging
All user-provided data must be sanitized before logging to prevent log injection attacks:
# BAD - Direct logging of user input
log.info("User input received", data=request.POST)
# GOOD - Use structured logging with sanitized data
log.info("User registration attempt", username=username[:50], ip_address=get_client_ip(request))
3. Use Structured Logging
Our structlog configuration includes automatic sanitization. Use structured logging instead of string formatting:
# BAD - String formatting vulnerable to injection
log.info(f"User {username} logged in")
# GOOD - Structured logging with automatic sanitization
log.info("User login successful", username=username, user_id=user.id)
4. Log What's Necessary for Security
DO Log:
- Authentication attempts (success/failure)
- Authorization failures
- Input validation failures
- Security-relevant application events
- Administrative actions
- Data access patterns
DON'T Log:
- Full request/response bodies
- Sensitive data fields
- Internal application state details
5. Implementation Examples
Authentication Logging
# Login success
log.info("User authentication successful",
user_id=user.id,
ip_address=get_client_ip(request),
user_agent_hash=hashlib.sha256(request.META.get('HTTP_USER_AGENT', '').encode()).hexdigest()[:16])
# Login failure
log.warn("Authentication failed",
username=username[:50], # Truncate to prevent log pollution
ip_address=get_client_ip(request),
failure_reason="invalid_credentials")
API Request Logging
# API access
log.info("API request",
endpoint=request.path,
method=request.method,
user_id=getattr(request.user, 'id', None),
status_code=response.status_code,
response_time_ms=response_time)
Error Logging
# System errors
log.error("Database connection failed",
operation="user_lookup",
error_code="DB_TIMEOUT",
retry_count=retry_count)
6. Log Sanitizer Configuration
Our log sanitizer automatically removes:
- CRLF injection attempts (
\r\n,%0a,%0d) - Control characters that could affect log parsing
- Fields containing sensitive patterns (token, password, secret, etc.)
- Overly long strings (truncated to 1000 chars)
Common Vulnerabilities to Avoid
CWE-117: Log Injection
# VULNERABLE
log.info(f"Processing request: {user_input}")
# SECURE
log.info("Processing user request", request_id=generate_request_id(), user_id=user.id)
Information Disclosure
# VULNERABLE
log.debug("Full user object", user=user.__dict__)
# SECURE
log.debug("User operation", user_id=user.id, operation="profile_update")
Log Pollution
# VULNERABLE - Can fill logs with junk
log.info("User message", message=untrusted_input)
# SECURE - Truncate and sanitize
log.info("User message received",
message_length=len(untrusted_input),
message_hash=hashlib.sha256(untrusted_input.encode()).hexdigest()[:16])
Testing Logging Security
Regularly test for:
- Log injection attempts in all input fields
- Sensitive data exposure in log files
- Log parsing errors due to malformed input
- Performance impact of logging under load
Compliance
This logging approach helps meet:
- OWASP Logging Cheat Sheet recommendations
- CWE-117 prevention guidelines
- GDPR data minimization principles
- SOC 2 logging requirements