Back to Blog

Web Security Best Practices: Protecting Your Django Application

admin
November 26, 2025 3 min read
191 views
Essential security measures every Django developer should implement to protect their applications from common vulnerabilities.

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!

Comments (0)

Please login to leave a comment.

No comments yet. Be the first to comment!