Introduction
Django is one of the most security-conscious web frameworks available, providing built-in protections against the most common web vulnerabilities. This lesson covers Django's security architecture, essential production settings, and the tools you need to audit your deployment.
Key Concepts
- Defense in Depth: Django layers multiple protections so a single failure doesn't compromise the whole system.
- Secure by Default: Most dangerous features (DEBUG, wildcard ALLOWED_HOSTS) must be explicitly enabled.
- manage.py check --deploy: Built-in command that audits your settings against known security risks.
- OWASP Top 10: Industry-standard list of the most critical web application security risks.
Real World Context
Misconfigured Django deployments are routinely discovered through automated scanners. A single forgotten DEBUG=True in production leaks your entire settings module, database credentials, and source code to anyone who triggers an error. Running check --deploy before every release catches these issues before attackers do.
Deep Dive
Security Checklist
bash# Run Django's deployment security check python manage.py check --deploy
Essential Security Settings
python# settings/production.py # NEVER run with DEBUG=True in production DEBUG = False # Secret key - use environment variable, never commit SECRET_KEY = os.environ['DJANGO_SECRET_KEY'] # Only allow your domain(s) ALLOWED_HOSTS = ['example.com', 'www.example.com'] # Force HTTPS SECURE_SSL_REDIRECT = True # HSTS - Tell browsers to only use HTTPS SECURE_HSTS_SECONDS = 31536000 # 1 year SECURE_HSTS_INCLUDE_SUBDOMAINS = True SECURE_HSTS_PRELOAD = True # Secure cookies SESSION_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True SESSION_COOKIE_HTTPONLY = True CSRF_COOKIE_HTTPONLY = False # Keep False if using cookie-based AJAX CSRF pattern # Prevent clickjacking X_FRAME_OPTIONS = 'DENY' # Note: SECURE_BROWSER_XSS_FILTER was removed in Django 4.0 # Modern protection uses Content Security Policy instead (see SECURE_CSP) # SECURE_CSP is configured via django.middleware.csp.ContentSecurityPolicyMiddleware # Prevent MIME type sniffing SECURE_CONTENT_TYPE_NOSNIFF = True # Referrer policy SECURE_REFERRER_POLICY = 'strict-origin-when-cross-origin'
Security Middleware
python# Ensure security middleware is enabled MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', # First! 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
Secret Key Generation
python# Generate a new secret key from django.core.management.utils import get_random_secret_key print(get_random_secret_key()) # Or using Python import secrets print(secrets.token_urlsafe(50))
OWASP Top 10 and Django
Django provides protection against many OWASP Top 10 vulnerabilities:
- Injection - ORM prevents SQL injection
- Broken Authentication - Built-in auth with secure defaults
- Sensitive Data Exposure - HTTPS enforcement, secure cookies
- XML External Entities - Not applicable (Django doesn't parse XML by default)
- Broken Access Control - Permission system, decorators
- Security Misconfiguration - manage.py check --deploy
- XSS - Template auto-escaping
- Insecure Deserialization - Signed cookies, careful with pickle
- Using Components with Known Vulnerabilities - Keep Django updated
- Insufficient Logging - Django logging framework
Keeping Django Updated
bash# Check for security updates pip list --outdated # Check for known vulnerabilities pip install safety safety scan # Or pip-audit pip install pip-audit pip-audit
Common Pitfalls
- Leaving DEBUG=True in production — Exposes stack traces, settings, and SQL queries to anyone who triggers an error.
- Forgetting to run check --deploy — Many misconfigurations only surface under this flag, not during normal development.
- Ordering middleware incorrectly — SecurityMiddleware must be first; placing it later means earlier middleware operates without its protections.
Best Practices
- Automate check --deploy in CI/CD — Fail the build if security checks don't pass.
- Use environment variables for secrets — Never hardcode SECRET_KEY or database credentials.
- Keep Django and dependencies updated — Subscribe to Django's security mailing list for patch notifications.
Summary
- Django ships with strong defaults, but production requires explicit configuration of HTTPS, cookies, and host validation.
- Run
manage.py check --deploybefore every release to catch misconfigurations. - Layer your protections: middleware ordering, secure cookies, HSTS, and CSP all work together.
Code Examples
python
# settings/production.py
import os
DEBUG = False
SECRET_KEY = os.environ['DJANGO_SECRET_KEY']
ALLOWED_HOSTS = ['example.com', 'www.example.com']
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = 'DENY'