Web Security Best Practices: Protecting Your Django Application
Web Security Best Practices for Django
Security isn't optional—it's essential. Let's explore how to protect your Django applications from common threats.
Django's Built-in Security
Django comes with excellent security features:
- CSRF protection
- XSS prevention
- SQL injection protection
- Clickjacking protection
- SSL/HTTPS enforcement
Make sure they're enabled!
1. Environment Variables for Secrets
Never commit secrets to version control:
# settings.py
import os
from dotenv import load_dotenv
load_dotenv()
SECRET_KEY = os.environ.get('SECRET_KEY')
DEBUG = os.environ.get('DEBUG', 'False') == 'True'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get('DB_NAME'),
'USER': os.environ.get('DB_USER'),
'PASSWORD': os.environ.get('DB_PASSWORD'),
'HOST': os.environ.get('DB_HOST'),
}
}
2. HTTPS Everywhere
Enforce HTTPS in production:
# settings.py (production)
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000 # 1 year
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
3. Content Security Policy
Prevent XSS with CSP headers:
# Using django-csp
CSP_DEFAULT_SRC = ("'self'",)
CSP_SCRIPT_SRC = ("'self'", 'cdn.example.com')
CSP_STYLE_SRC = ("'self'", "'unsafe-inline'", 'fonts.googleapis.com')
CSP_IMG_SRC = ("'self'", 'data:', 'cdn.example.com')
CSP_FONT_SRC = ("'self'", 'fonts.gstatic.com')
4. Rate Limiting
Protect against brute force attacks:
# Using django-ratelimit
from django_ratelimit.decorators import ratelimit
@ratelimit(key='ip', rate='5/m', method='POST', block=True)
def login_view(request):
# Login logic here
pass
@ratelimit(key='ip', rate='100/h', method='GET')
def api_view(request):
if getattr(request, 'limited', False):
return JsonResponse({'error': 'Rate limit exceeded'}, status=429)
# Normal logic
5. Password Security
Use strong password validation:
# settings.py
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {'min_length': 12},
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Use Argon2 for password hashing
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
]
6. SQL Injection Prevention
Always use parameterized queries:
# BAD - vulnerable to SQL injection
User.objects.raw(f"SELECT * FROM users WHERE name = '{name}'")
# GOOD - parameterized
User.objects.raw("SELECT * FROM users WHERE name = %s", [name])
# BEST - use ORM
User.objects.filter(name=name)
7. File Upload Security
Validate uploaded files carefully:
from django.core.validators import FileExtensionValidator
class Document(models.Model):
file = models.FileField(
upload_to='documents/',
validators=[
FileExtensionValidator(allowed_extensions=['pdf', 'doc', 'docx']),
]
)
def validate_file(file):
# Check file size
if file.size > 10 * 1024 * 1024: # 10 MB
raise ValidationError("File too large")
# Verify content type
import magic
file_type = magic.from_buffer(file.read(1024), mime=True)
file.seek(0)
allowed_types = ['application/pdf', 'application/msword']
if file_type not in allowed_types:
raise ValidationError("Invalid file type")
8. API Security
Protect your API endpoints:
from rest_framework.throttling import AnonRateThrottle, UserRateThrottle
class BurstRateThrottle(UserRateThrottle):
rate = '60/min'
class SustainedRateThrottle(UserRateThrottle):
rate = '1000/day'
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'myapp.throttling.BurstRateThrottle',
'myapp.throttling.SustainedRateThrottle',
],
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
}
9. Logging & Monitoring
Log security events:
LOGGING = {
'version': 1,
'handlers': {
'security': {
'class': 'logging.handlers.RotatingFileHandler',
'filename': '/var/log/django/security.log',
'maxBytes': 10485760, # 10 MB
'backupCount': 5,
},
},
'loggers': {
'django.security': {
'handlers': ['security'],
'level': 'WARNING',
},
},
}
10. Security Headers
Add protective headers:
# Middleware
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
# ... other middleware
]
# Security settings
SECURE_BROWSER_XSS_FILTER = True
X_FRAME_OPTIONS = 'DENY'
SECURE_CONTENT_TYPE_NOSNIFF = True
Security Checklist
Before deploying:
- [ ] DEBUG = False in production
- [ ] SECRET_KEY is unique and secret
- [ ] HTTPS is enforced
- [ ] Database credentials are in environment variables
- [ ] Admin URL is customized (not /admin/)
- [ ] Rate limiting is configured
- [ ] File uploads are validated
- [ ] Security headers are set
- [ ] Dependencies are up to date
Conclusion
Security requires constant vigilance. Stay updated on Django security releases and regularly audit your application for vulnerabilities.
For more security resources, check out our security-focused templates!