Security and CSRF Protection in the Routing Layer
Security in the routing layer is managed by a suite of middleware that intercepts requests and responses to enforce safety protocols. These components protect against Cross-Site Request Forgery (CSRF), clickjacking, and insecure transport, ensuring that security policies are applied consistently before a request reaches a view and after a response is generated.
CSRF Protection
The CsrfViewMiddleware in django/middleware/csrf.py provides protection against CSRF attacks by ensuring that state-changing requests (like POST, PUT, or DELETE) contain a valid, non-predictable token.
Token Storage and Retrieval
The middleware supports two primary ways of storing the CSRF secret, controlled by the CSRF_USE_SESSIONS setting. The _get_secret method handles this logic:
def _get_secret(self, request):
if settings.CSRF_USE_SESSIONS:
try:
csrf_secret = request.session.get(CSRF_SESSION_KEY)
except AttributeError:
raise ImproperlyConfigured(
"CSRF_USE_SESSIONS is enabled, but request.session is not set..."
)
else:
try:
csrf_secret = request.COOKIES[settings.CSRF_COOKIE_NAME]
except KeyError:
csrf_secret = None
# ... logic to unmask the token if necessary ...
return csrf_secret
If CSRF_USE_SESSIONS is enabled, SessionMiddleware must appear before CsrfViewMiddleware in the MIDDLEWARE setting.
The Validation Lifecycle
CSRF validation occurs in the process_view method. The middleware follows a specific sequence to determine if a request is safe:
- Exemption Check: If the view is marked with
csrf_exempt(via the@csrf_exemptdecorator), validation is skipped. - Safe Method Check: Requests using "safe" methods (GET, HEAD, OPTIONS, TRACE) are automatically accepted via
_accept. - Origin Verification: For all requests, if an
HTTP_ORIGINheader is present, it is verified againstsettings.CSRF_TRUSTED_ORIGINSusing_origin_verified. - Strict Referer Check (HTTPS): For HTTPS requests, if the
Originheader is missing, the middleware performs a strict check of theHTTP_REFERERheader in_check_referer. This protects against Man-In-The-Middle (MITM) attacks that could otherwise bypass CSRF protections on secure connections. - Token Verification: Finally,
_check_tokencompares the token provided in the request (either inrequest.POSTor the header defined byCSRF_HEADER_NAME) against the stored secret.
Token Masking
To mitigate SSL BREACH attacks, Django uses masked tokens. The _get_secret method unmasks the stored secret using _unmask_cipher_token, and the middleware ensures that the token sent to the client is scrambled differently on every request while still representing the same underlying secret.
Security Hardening and SSL
The SecurityMiddleware in django/middleware/security.py handles transport-level security and various browser-side security headers.
SSL Redirection
If SECURE_SSL_REDIRECT is enabled, the middleware intercepts non-HTTPS requests in process_request and returns an HttpResponsePermanentRedirect to the HTTPS version of the URL:
def process_request(self, request):
path = request.path.lstrip("/")
if (
self.redirect
and not request.is_secure()
and not any(pattern.search(path) for pattern in self.redirect_exempt)
):
host = self.redirect_host or request.get_host()
return HttpResponsePermanentRedirect(
"https://%s%s" % (host, request.get_full_path())
)
Security Headers
In process_response, the middleware injects several headers to harden the browser's security posture:
- Strict-Transport-Security (HSTS): Controlled by
SECURE_HSTS_SECONDS. It instructs the browser to only communicate with the server over HTTPS for a specified duration. - X-Content-Type-Options: If
SECURE_CONTENT_TYPE_NOSNIFFis True, it sets this header tonosniff, preventing the browser from "sniffing" the content type and executing files as a different MIME type. - Referrer-Policy: Configured via
SECURE_REFERRER_POLICYto control how much information is included in theRefererheader when navigating away from the site. - Cross-Origin-Opener-Policy (COOP): Configured via
SECURE_CROSS_ORIGIN_OPENER_POLICYto isolate the browsing context from other windows.
Clickjacking Protection
The XFrameOptionsMiddleware in django/middleware/clickjacking.py prevents the site from being rendered inside an <iframe_>, <frame_>, or <object_> on other sites, which is a common vector for clickjacking attacks.
By default, the middleware sets the X-Frame-Options header to DENY. This behavior can be customized via the X_FRAME_OPTIONS setting (e.g., to SAMEORIGIN).
def process_response(self, request, response):
# Don't set it if it's already in the response or exempt
if response.get("X-Frame-Options") is not None:
return response
if getattr(response, "xframe_options_exempt", False):
return response
response.headers["X-Frame-Options"] = self.get_xframe_options_value(request, response)
return response
Granular Control with Decorators
While middleware provides global protection, specific views often require different security configurations. Django provides decorators in django/views/decorators/csrf.py and django/views/decorators/clickjacking.py to modify middleware behavior per-view.
@csrf_exempt: Marks a view to skip theCsrfViewMiddlewarecheck. This is often used for APIs that handle their own authentication (like Webhooks).@csrf_protect: Forces CSRF protection on a view, even if the middleware is not globally enabled.@xframe_options_exempt: Prevents theXFrameOptionsMiddlewarefrom setting the header, allowing the view to be framed.@xframe_options_sameorigin: Overrides the global policy to allow the view to be framed only by pages on the same domain.
These decorators work by setting attributes on the view function (e.g., view_func.csrf_exempt = True), which the middleware then inspects during the process_view or process_response phases.