Logging Monitoring Failures in Django
How Logging Monitoring Failures Manifests in Django
Logging monitoring failures in Django occur when sensitive data is inadvertently logged or when security-relevant events are not properly captured. This manifests in several critical ways:
- Debug mode data exposure: When DEBUG=True in production, Django's logging captures full request/response data including query parameters, headers, and POST bodies. Attackers can trigger error pages that reveal stack traces with database credentials and internal paths.
- SQL query logging: Django's ORM logs raw SQL queries by default in development, which can include PII, passwords, or API keys embedded in query parameters or result sets.
- Missing security event logging: Failed authentication attempts, privilege escalation attempts, and sensitive data access are not logged, preventing security teams from detecting attacks.
- Exception logging with sensitive data: Django's default logging configuration may log exception messages that contain user credentials, tokens, or personal information.
- Admin interface exposure: Django admin logs sensitive operations without proper access controls, allowing unauthorized users to view audit trails.
A common vulnerability pattern in Django applications is the combination of DEBUG=True with inadequate logging configuration. Consider this typical setup:
# settings.py (vulnerable)
DEBUG = True # Never do this in production
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'root': {
'handlers': ['console'],
'level': 'DEBUG',
},
}
With this configuration, any exception triggers Django's technical error page, which includes:
- Full stack traces with file paths
- Local variables containing sensitive data
- Database connection details
- Internal server paths
Attackers can exploit this by triggering exceptions through malformed requests, causing the server to leak internal implementation details.
Django-Specific Detection
Detecting logging monitoring failures in Django requires examining both configuration and runtime behavior. Here are Django-specific detection methods:
Configuration Analysis
Check Django settings for security misconfigurations:
# settings.py - check for these vulnerabilities
if DEBUG and not DEBUG_MODE_ALLOWED:
raise ImproperlyConfigured(
"DEBUG = True in production exposes sensitive data"
)
# Verify logging doesn't expose sensitive data
if 'password' in LOGGING.get('format', '').lower():
raise ImproperlyConfigured(
"Logging format should not include passwords"
)
Runtime Scanning with middleBrick
middleBrick's Django-specific scanning identifies logging vulnerabilities by:
- Testing for DEBUG=True in production environments
- Analyzing logging configurations for sensitive data exposure
- Checking for missing security event logging
- Verifying proper exception handling in Django views
Example middleBrick scan for Django logging issues:
# Scan Django API endpoint
middlebrick scan https://api.example.com/auth/login \
--output json | jq '.findings[] | select(.category == "Logging")'
The scanner tests for common Django logging failures:
{
"category": "Logging",
"severity": "high",
"finding": "DEBUG mode enabled in production",
"remediation": "Set DEBUG = False and configure proper logging handlers"
}
Middleware-Based Detection
Implement Django middleware to detect logging issues at runtime:
class LoggingSecurityMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Check for sensitive data in request
if 'password' in request.body.decode(errors='ignore').lower():
logger.warning(
"Potential password exposure in request: %s",
request.path,
extra={'sensitive_data': True}
)
response = self.get_response(request)
# Check response for sensitive data
if 'token' in response.content.decode(errors='ignore').lower():
logger.warning(
"Potential token exposure in response: %s",
request.path,
extra={'sensitive_data': True}
)
return response
Django-Specific Remediation
Securing Django logging requires proper configuration and custom middleware. Here are Django-specific remediation strategies:
Production-Ready Logging Configuration
Configure Django logging for production with security in mind:
# settings.py - production logging
import os
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'secure': {
'format': '%(asctime)s %(levelname)s %(name)s %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S'
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'secure',
},
'file': {
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(BASE_DIR, 'logs', 'django.log'),
'maxBytes': 1024 * 1024 * 5, # 5MB
'backupCount': 5,
'formatter': 'secure',
'level': 'INFO',
},
},
'loggers': {
'django': {
'handlers': ['file'],
'level': 'INFO',
'propagate': False,
},
'django.security': {
'handlers': ['file'],
'level': 'WARNING',
'propagate': False,
},
},
}
# Security settings
DEBUG = bool(os.getenv('DJANGO_DEBUG', 'false').lower() == 'true')
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
Custom Security Middleware
Implement middleware to prevent sensitive data logging:
import re
from django.utils.deprecation import MiddlewareMixin
class SecurityLoggingMiddleware(MiddlewareMixin):
SENSITIVE_PATTERNS = [
re.compile(r'password=([^&]+)', re.IGNORECASE),
re.compile(r'token=([^&]+)', re.IGNORECASE),
re.compile(r'api[_-]?key=([^&]+)', re.IGNORECASE),
re.compile(r'secret=([^&]+)', re.IGNORECASE),
]
def process_request(self, request):
# Check for sensitive data in request
if hasattr(request, 'body'):
body = request.body.decode(errors='ignore')
if any(pattern.search(body) for pattern in self.SENSITIVE_PATTERNS):
# Log without exposing sensitive data
logger.warning(
f"Potential sensitive data in request to {request.path}",
extra={'request_path': request.path}
)
# Optionally reject the request
# return HttpResponseForbidden('Request contains sensitive data')
def process_exception(self, request, exception):
# Custom exception handling without exposing sensitive data
if settings.DEBUG:
# In development, show detailed errors
return None
else:
# In production, log and show generic error
logger.error(
f"Unhandled exception in {request.path}: {exception}",
exc_info=True,
extra={'request_path': request.path}
)
return HttpResponseServerError(
'An error occurred. Please try again later.'
)
Security Event Logging
Log security-relevant events in Django:
from django.contrib.auth import get_user_model
from django.db.models import signals
class SecurityLogger:
@staticmethod
def log_failed_login(sender, credentials, **kwargs):
logger.warning(
'Failed login attempt',
extra={
'username': credentials.get('username'),
'ip_address': get_client_ip(),
'user_agent': request.META.get('HTTP_USER_AGENT', 'unknown'),
'attempt': 'login_failed'
}
)
@staticmethod
def log_password_change(user, **kwargs):
logger.info(
'Password changed',
extra={
'user_id': user.id,
'username': user.username,
'ip_address': get_client_ip(),
'event': 'password_changed'
}
)
# Connect signals
signals.failed_login.connect(SecurityLogger.log_failed_login)
# Connect password change signal if using custom user model