Course outline

Django / Correctness and production evidence / Lesson 15

Security settings and trust boundaries

Keep host, origin, transport, input, and permission guarantees separate while applying a reviewed deployment configuration.

About 4 min readContent reviewed
On this page

Lesson goals

  • Security follows capabilities and data flow
  • Validate a redirect destination with the dedicated helper
  • Test negative cases at the real boundaries

Security follows capabilities and data flow

List what a caller can control and where each value goes: HTML text, a URL, raw SQL, a file path, a session, or a privileged operation. Each context has its own validation and encoding rules. Django’s normal template escaping is useful for HTML text but cannot validate a redirect destination or authorize a database record. ORM parameters protect ordinary value construction, while arbitrary raw SQL and structural ordering choices require separate care. Avoid one vague sanitize function for every boundary. Keep secrets outside source and client-delivered state, and log only the context needed to investigate a failure without exposing credentials or personal payloads.

Host and CSRF origin settings answer different questions

ALLOWED_HOSTS constrains accepted host names under Django’s host validation; it is not a list of every trusted browser origin or an authorization policy. CSRF_TRUSTED_ORIGINS applies to the CSRF origin-check mechanism and includes scheme-bearing origins under the documented rules. Broad wildcards can erase the intended boundary. HTTPS detection also depends on how a reviewed reverse proxy forwards requests. Only configure trusted forwarded-header interpretation when the proxy strips or controls untrusted client headers as required. A mistaken trust setting can undermine secure redirects and origin checks. Test the actual proxy path rather than copying one deployment recipe blindly.

Validate a redirect destination with the dedicated helper

The first complete example checks three synthetic destinations with url_has_allowed_host_and_scheme. It accepts the approved HTTPS origin and an internal relative path, while rejecting another host. The helper supports a redirect allow policy; it is not an SSRF validator for a server that fetches arbitrary URLs, where DNS, redirects, internal networks, and other capabilities require a stronger design. Do not use a text prefix comparison that can confuse a trusted host with a longer hostile hostname. Authentication views and custom next parameters should preserve a documented destination policy, and tests should include malformed and protocol-relative candidates where the application accepts them.

python Isolated executable example
from django.conf import settings
settings.configure(
    SECRET_KEY="synthetic-course-key",
    USE_I18N=False, USE_TZ=True,
    INSTALLED_APPS=[],
    DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}},
    DEFAULT_AUTO_FIELD="django.db.models.AutoField",
    ALLOWED_HOSTS=["testserver"],
)
import django
django.setup()

from django.utils.http import url_has_allowed_host_and_scheme
allowed = {"learn.example.test"}
for destination in ["https://learn.example.test/courses/", "https://other.example.test/", "/courses/"]:
    print(url_has_allowed_host_and_scheme(destination, allowed_hosts=allowed, require_https=True))
Expected output
True
False
True

This local computation validates a redirect policy only. It performs no DNS lookup, request, or navigation.

Make production settings explicit without embedding secrets

The second fragment illustrates a settings boundary with DEBUG disabled, a deliberately narrow host, a secret read from an external environment facility, and secure cookie and redirect intent. It is non-runnable configuration, not a complete deployment or an instruction to set these values on a configured application. The public host is synthetic. HTTPS termination, trusted proxy settings, session storage, static/media serving, error reporting, and recovery must be reviewed together. HSTS and related persistent browser policies need deliberate rollout and rollback analysis. Turning DEBUG off does not make a deployment secure if the rest of those assumptions are wrong.

python Application-context fragment
import os

DEBUG = False
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
ALLOWED_HOSTS = ["learn.example.test"]
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
Expected output
Configuration intent: no debug output, explicit host, external secret, and HTTPS-only cookie/redirect policy.

Illustrative production-settings fragment requiring a reviewed deployment and secret source. It deliberately omits an unverified proxy-header trust setting and is not executed.

Test negative cases at the real boundaries

Verify allowed and rejected hosts, expected secure redirects, CSRF failures, scoped resource access, safe template output, and sensitive-field absence in responses. Treat user-uploaded content as untrusted media and serve it under an appropriate storage and content policy rather than as executable application assets. Keep supported Django and dependency patches under review; an older tutorial can reference removed settings or newer-version APIs. Django 5.2 does not gain a later release’s feature merely because current unversioned documentation describes it. Use the versioned references here, and never disable protection globally to make an unexplained request succeed.

Common errors and fixes

ALLOWED_HOSTS is treated as a user permission list

Why it happens
Host validation was confused with authorization.
How to fix it
Keep host, origin, and resource checks separate.
Prevent it next time
Test each boundary independently.

Secure redirects loop behind a proxy

Why it happens
HTTPS interpretation does not match the trusted proxy arrangement.
How to fix it
Review TLS termination and controlled forwarded headers.
Prevent it next time
Validate through the actual isolated deployment path.

A redirect allow check is reused as SSRF protection

Why it happens
Browser navigation and server fetch capabilities were conflated.
How to fix it
Design the server-fetch boundary separately.
Prevent it next time
Document the helper’s limited guarantee.

Check your understanding

1. Why should a trusted proxy header not be enabled merely because a copied guide recommends it?

Show worked solution

Its safety depends on the proxy controlling or stripping untrusted incoming values and on the actual TLS termination path. Verify those properties and the framework setting together. Otherwise client-supplied headers can influence security decisions the application incorrectly treats as trusted transport evidence.

2. What should a rich-text feature do instead of marking every stored string safe?

Show worked solution

Define the supported markup and a reviewed sanitization boundary, retain normal escaping for ordinary text, and test the resulting output contexts. Storage does not make user-authored content trusted. Keep server authorization and data minimization independent of the HTML rendering decision.

Key takeaways

  • Security guarantees are context-specific.
  • Trust proxy evidence only under a verified arrangement.
  • Versioned settings and negative tests matter.

Official references