Skip to content

Security

Django includes robust protections against common web vulnerabilities. Most are enabled by default but require correct configuration in production.

Cross-Site Scripting (XSS)

Django automatically escapes HTML in templates (< becomes &lt;), preventing injected scripts from running.

  • Bypassing safely: Only use {{ var|safe }} or mark_safe() if you have sanitized the input yourself (e.g., via bleach).
  • Contexts escaping doesn't cover: Beware of unquoted attributes (class={{ var }}) or <script> tags. Use the json_script filter to pass data to JavaScript safely.

Cross-Site Request Forgery (CSRF)

CsrfViewMiddleware requires a token for all POST/PUT/DELETE requests to stop attackers from forging actions on a logged-in user's behalf.

  • HTML Forms: Always include {% csrf_token %} inside your <form>.
  • AJAX/Fetch: Read the token from the csrftoken cookie and send it in the X-CSRFToken header.
  • Cross-Origin: If submitting from a different frontend domain or subdomain, add it to CSRF_TRUSTED_ORIGINS.

SQL Injection

Django's ORM parameterizes all queries automatically, neutralizing injection attempts.

  • Raw SQL Danger: Never use string formatting (% or f-strings) to build SQL queries. Always pass variables as parameters:

    python
    # SAFE
    User.objects.raw("SELECT * FROM auth_user WHERE username = %s", [name])

Clickjacking

XFrameOptionsMiddleware blocks your site from being loaded in an invisible iframe via the X_FRAME_OPTIONS = "DENY" setting. If framing is explicitly required for a specific view, use the @xframe_options_exempt decorator.

HTTPS & Host Validation

Encrypting traffic is critical to protect session cookies and CSRF tokens from network snooping.

  • HTTPS Enforcement: Set SECURE_SSL_REDIRECT = True, SESSION_COOKIE_SECURE = True, and CSRF_COOKIE_SECURE = True.
  • HSTS: Enable SECURE_HSTS_SECONDS to force browsers to always use HTTPS for your domain (start with a small value before increasing).
  • Proxies: If behind a load balancer terminating TLS, set SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") so Django knows the original request was secure.
  • Host Header: ALLOWED_HOSTS is mandatory when DEBUG = False to prevent host-header poisoning.

Content Security Policy (CSP)

Use django.middleware.csp.ContentSecurityPolicyMiddleware to restrict where scripts and resources can load from, providing a second line of defence against XSS.

python
from django.utils.csp import CSP

SECURE_CSP = {
    "default-src": [CSP.SELF],
    "script-src": [CSP.SELF, CSP.NONCE], # Allows inline scripts with a valid nonce
}

Cryptographic Signing

SECRET_KEY underpins session integrity, password reset tokens, and signed cookies. Keep it secret, load it from the environment, and use SECRET_KEY_FALLBACKS to rotate it safely.

You can use the same machinery for your own expiring links:

python
from django.core.signing import TimestampSigner

token = TimestampSigner().sign("[email protected]")

Security Best Practices

  • Uploads: Never execute user-uploaded files. Restrict extensions, sizes, and serve media from a separate domain.
  • Admin: Protect the admin by moving it off the default /admin/ path, restricting IP access, and enabling Multi-Factor Authentication (MFA).
  • Checklist: Run python manage.py check --deploy to audit your production settings. Keep Django updated to receive security patches.