Log Injection in Django with Basic Auth
Log Injection in Django with Basic Auth — how this specific combination creates or exposes the vulnerability
Log injection occurs when untrusted data is written directly into log entries without sanitization, enabling log forging or log injection attacks. In Django, combining Basic Authentication with improper log handling creates conditions where an attacker can manipulate log output. Basic Authentication sends credentials as base64-encoded, easily decoded headers; if the application logs the username from request.META or the decoded authorization header without validation, an attacker can inject newline or other control characters to corrupt log structure.
Consider a logging pattern that records the authenticated username as part of an informational message:
import logging
logger = logging.getLogger(__name__)
def my_view(request):
auth_header = request.META.get('HTTP_AUTHORIZATION', '')
# Risky: using decoded username directly in logs
if auth_header.startswith('Basic '):
import base64
decoded = base64.b64decode(auth_header.split(' ')[1]).decode('utf-8')
username, password = decoded.split(':', 1)
logger.info(f'User {username} accessed the endpoint') # injection risk
An attacker can supply a crafted username containing newline sequences (e.g., alice\nX-Forwarded-For: 10.0.0.1) which, when logged, can forge additional log lines or obscure real events. This can complicate incident response and enable log-based injection chains when logs are ingested by monitoring tools. The risk is elevated when logs are centralized and parsed by regex-based systems, as injected newlines may break expected formats. In a security risk assessment, such patterns would be flagged under Data Exposure and Unsafe Consumption checks because logs may inadvertently reveal credentials or be used to mislead operators.
Moreover, if the application emits structured logs (e.g., JSON) without proper escaping, newline injection can break JSON validity, leading to parsing failures or enabling injection of additional fields. For example:
logger.info('{"user": "%s", "action": "login"}' % username) # vulnerable if username contains quotes or newlines
This can result in malformed logs that evade detection rules or create blind spots. middleBrick’s LLM/AI Security and Unsafe Consumption checks highlight how log injection can distort observability pipelines. Remediation focuses on sanitizing data before it reaches log statements and avoiding direct concatenation of user-controlled values into log messages.
Basic Auth-Specific Remediation in Django — concrete code fixes
Secure logging in Django with Basic Authentication requires strict input validation, avoiding raw concatenation, and using structured, escaped logging. Instead of decoding and logging the username directly, treat the header as opaque for logging purposes or extract and sanitize only necessary parts.
Secure logging pattern
Use Django’s built-in HttpRequest attributes and sanitize any extracted data. For example, if you need to log usernames, hash or truncate them, and ensure no control characters are present:
import logging
import re
logger = logging.getLogger(__name__)
def sanitize_username(value):
# Remove newlines, carriage returns, and other control-like chars
return re.sub(r'[\r\n\t]', '_', value)
def my_view(request):
auth_header = request.META.get('HTTP_AUTHORIZATION', '')
if auth_header.startswith('Basic '):
import base64
decoded = base64.b64decode(auth_header.split(' ')[1]).decode('utf-8')
username, password = decoded.split(':', 1)
safe_username = sanitize_username(username)
# Log a safe, sanitized representation
logger.info('User accessed endpoint', extra={'user_hash': hash(safe_username)})
Using extra with structured fields avoids injecting raw text into the message template and keeps logs parseable. Ensure your logging formatter is configured to handle custom fields safely.
Django Basic Auth example with secure headers handling
Here is a complete, secure example that validates the presence of Basic Auth, decodes safely, and avoids logging sensitive or untrusted raw values:
import base64
import logging
from django.http import HttpResponse, JsonResponse
from django.views import View
logger = logging.getLogger(__name__)
class SecureEndpointView(View):
def dispatch(self, request, *args, **kwargs):
auth = request.META.get('HTTP_AUTHORIZATION', '')
if not auth.startswith('Basic '):
return JsonResponse({'error': 'Unauthorized'}, status=401)
try:
decoded = base64.b64decode(auth.split(' ')[1]).decode('utf-8')
username, password = decoded.split(':', 1)
# Validate credentials via Django auth (example stub)
if self.is_valid_user(username, password):
# Avoid logging password; log only sanitized username or an ID
safe_user = ''.join(c for c in username if c.isalnum())
logger.info('Authentication success', extra={'user': safe_user})
return self.handle_request(request, *args, **kwargs)
return JsonResponse({'error': 'Invalid credentials'}, status=403)
except (ValueError, UnicodeDecodeError, base64.binascii.Error):
logger.warning('Malformed Basic Auth header')
return JsonResponse({'error': 'Bad request'}, status=400)
def is_valid_user(self, username, password):
# Replace with actual Django user validation, e.g., authenticate()
return True
def handle_request(self, request, *args, **kwargs):
return JsonResponse({'status': 'ok'})
This pattern prevents newline and control-character injection and avoids logging passwords. It aligns with secure logging practices recommended for applications handling credentials. middleBrick’s CLI can be used to verify that no authentication bypass or data exposure findings appear after implementing these changes.
Finally, consider avoiding logging usernames altogether in high-security contexts and rely on request IDs or transaction hashes for traceability. If you use the middleBrick Dashboard, you can track how security scores evolve as you apply these fixes and integrate the GitHub Action to prevent regressions in CI/CD.