Many of Django’s settings predate request.headers, which provides a less-WSGI-specific interface to HTTP headers. The settings instead use keys of request.META (which comes from the WSGI environ). Even those added after request.headers use the request.META syntax by convention.
This rarely causes problems, but can cause confusion for new developers who have to provide a different spelling for headers to what they may be used to.
Additionally, the WSGI environ can be modified by the WSGI server directly, which isn’t true under ASGI. #36862 (Clarify RemoteUserMiddleware usage and deployment requirements under ASGI) – Django highlights that in its default configuration, RemoteUserMiddleware reads request.META["REMOTE_USER"] under WSGI, but request.META["HTTP_REMOTE_USER"] under ASGI. The former is safe, since it doesn’t correspond to a header, however the latter is “less safe” (I’m intentionally not calling it “unsafe”), since it does correspond to a header, and could lead to a vulnerability if the fronting proxy if the header isn’t properly sanitized.
Therefore, I propose changing Django’s settings to instead specify headers based on the verbatim header name (eg X-Bender) and reading from request.headers rather than request.META key (HTTP_X_BENDER). It avoids confusing when setting and keeps a consistent implementation between ASGI and WSGI (and future SGI’s). request.headers is derived from request.META but with appropriate normalisation, and is derived strictly from HTTP headers.
For ease and convenience, this change should be applied both to values from settings, but also any internal header references (perhaps in stages).
Solutionizing, to avoid this being a huge breaking change, settings would need to support both spellings:
_is_meta_header = re.compile(r"HTTP_[A-Z1-9_]+")
if _is_meta_header.match(settings.CSRF_HEADER_NAME):
# Raise deprecation warning
value = request.META[settings.CSRF_HEADER_NAME]
else:
value = request.headers[settings.CSRF_HEADER_NAME]
(Don’t read too much into the implementation. A regex may not be the best way, and the match should definitely be pre-computed. This is just an example to show it should follow a deprecation path).