Back to Rules
Django91% popularity

Django Security Best Practices

Implement comprehensive security measures including CSRF, XSS prevention, and SQL injection protection.

Django Software FoundationUpdated Mar 2, 2024

Overview

Django's security features provide substantial protection against common web vulnerabilities, but they require proper implementation and configuration. Understanding each layer of Django's security middleware and when to customize it ensures robust application security. CSRF protection in Django works through the {% csrf_token %} template tag and the csrf_exempt decorator sparingly. All POST forms must include csrf_token, and AJAX requests should include the X-CSRFToken header. The CsrfViewMiddleware processes every POST request, rejecting those without valid tokens. Custom views handling AJAX should use ensure_csrf_cookie when explicit token passing is impractical. XSS prevention relies on Django's auto-escaping in templates, but this protection disappears with |safe filters or mark_safe(). These should be used only when content is definitively sanitized. ContentSecurityPolicy headers provide additional defense by controlling what resources pages can load, preventing inline scripts and external resource hijacking. SQL injection is prevented by Django's ORM parameterization, but raw queries with extra() or raw() require extreme care. Never interpolate user input directly into SQL strings. The parameterized query pattern should be automatic—every database query should use ? or %s placeholders with separate parameter values. Password hashing uses PBKDF2 by default with 720000 iterations, meeting modern security standards. Custom hashers should only be used for migration compatibility, never for new implementations. Django's authentication views and forms already implement secure patterns—replacing them without equivalent security review creates vulnerabilities.

Code Example

.djangorules
from django.http import HttpResponse, HttpResponseForbidden
from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import csrf_protect, csrf_exempt
from django.shortcuts import render
from django.contrib.auth.decorators import login_required, permission_required
from django.middleware.security import SecurityMiddleware
from django.conf import settings
import hashlib
import secrets

# Secure file download with authentication
@login_required
@require_http_methods(["GET"])
def download_file(request, file_id):
    if not request.user.has_perm('documents.can_download'):
        return HttpResponseForbidden("Permission denied")

    file_obj = get_object_or_404(Document, id=file_id, owner=request.user)

    response = HttpResponse(file_obj.content, content_type='application/octet-stream')
    response['Content-Disposition'] = f'attachment; filename="{file_obj.name}"'
    response['X-Content-Type-Options'] = 'nosniff'
    response['Cache-Control'] = 'no-cache, no-store, must-revalidate'

    return response

# Secure API endpoint with CSRF and rate limiting
@csrf_protect
@require_http_methods(["POST"])
def api_submit(request):
    if not request.headers.get('X-Requested-With') == 'XMLHttpRequest':
        return HttpResponseForbidden("AJAX required")

    data = json.loads(request.body)

    if not data.get('captcha_token'):
        return JsonResponse({'error': 'CAPTCHA required'}, status=400)

    # Rate limiting using Django cache
    client_ip = request.META.get('REMOTE_ADDR')
    rate_key = f'rate_limit:{client_ip}'
    rate_count = cache.get(rate_key, 0)

    if rate_count >= settings.RATE_LIMIT_PER_MINUTE:
        return JsonResponse(
            {'error': 'Rate limit exceeded'},
            status=429,
            headers={'Retry-After': '60'}
        )

    cache.set(rate_key, rate_count + 1, 60)

    # Process with sanitized input
    serializer = SubmissionSerializer(data={
        'email': data.get('email'),
        'message': sanitize_html(data.get('message')),
    })

    if serializer.is_valid():
        serializer.save()
        return JsonResponse({'status': 'success'})
    return JsonResponse({'errors': serializer.errors}, status=400)

# Secure password reset with token
@csrf_protect
def password_reset_request(request):
    if request.method == 'POST':
        email = request.POST.get('email')
        try:
            user = User.objects.get(email=email)
            token = generate_secure_token()
            PasswordResetToken.objects.create(user=user, token=token)

            send_password_reset_email(user.email, token)
        except User.DoesNotExist:
            pass  # Don't reveal email existence

        return JsonResponse({'status': 'If email exists, reset link was sent'})

# Content Security Policy
class CSPMiddleware(SecurityMiddleware):
    async def __call__(self, scope, receive, send):
        response = await self.get_response(scope, receive, send)
        response['Content-Security-Policy'] = (
            "default-src 'self'; "
            "script-src 'self' 'nonce-{nonce}'; "
            "style-src 'self' 'unsafe-inline'; "
            "img-src 'self' data: https:; "
            "font-src 'self'; "
            "frame-ancestors 'none';"
        )
        return response

More Django Rules

DJANGO
89%

Django REST Framework Serializer Validation

Implement multi-layer validation in DRF serializers for robust API input handling and security.

djangorest-frameworkvalidation
from rest_framework import serializers
from django.contrib.auth import get_user_model
from django.utils import timezone
from .models import Project, T...
Mar 5, 2024by Tom Christie
View Rule
DJANGO
87%

Django QuerySet Optimization

Use select_related, prefetch_related, and only() to minimize database queries.

djangoormqueryset
from django.db.models import Prefetch, Count, Q
from django.shortcuts import render
from .models import Author, Book, Publisher

# Problematic: N+1 qu...
Feb 25, 2024by Aymeric Augustin
View Rule
DJANGO
89%

Django REST Framework Permissions

Implement granular permission systems with DRF permission classes and custom permission logic.

djangorest-frameworkpermissions
from rest_framework import permissions
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.gener...
Mar 25, 2024by Tom Christie
View Rule