#37025: Deprecate the prefixing of HTTP_ to header names in
RemoteUserMiddleware.aprocess_request()
-----------------------------+----------------------------------------
Reporter: Jacob Walls | Type: Bug
Status: new | Component: contrib.auth
Version: 5.2 | Severity: Normal
Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-----------------------------+----------------------------------------
When Django 5.2 added an async path to `RemoteUserMiddleware` (#35303), it
prefixed `HTTP_` to the provided (or default) header name before looking
it up in `request.META`.
I figure this was so that the default `REMOTE_USER` header would work out
of the box. (For WSGI compatibility, `ASGIRequest` maps all user provided
headers to `HTTP_`... variants.)
This has two problematic effects:
- It becomes tortured to document the security considerations, see recent
change in #36862:
> Under WSGI, this warning doesn’t apply to RemoteUserMiddleware in its
default configuration with header = "REMOTE_USER", since a key that
doesn’t start with HTTP_ in request.META can only be set by your WSGI
server, not directly from an HTTP request header. This warning does apply
by default on ASGI, because in the async path, the middleware prepends
HTTP_ to the defined header name before looking it up in request.META.
Notice how an implementation detail is leaking into an admonition, making
it harder to reason about what's potentially user-controlled (wait, under
ASGI, this other stuff is user-controlled, too!)
- Custom headers can't be supplied in a way that works with both WSGI &
ASGI:
If you try to do this the "right" way, and define `HTTP_MY_CUSTOM_HEADER`
as the header attribute, then `aprocess_request()` will lookup
`HTTP_HTTP_MY_CUSTOM_HEADER`. Forcing the 'wrong' version
(`CUSTOM_HEADER`) works on ASGI but breaks on WSGI, because no WSGI server
will set that.
----
I'm suggesting we should deprecate the current support for custom headers
without "HTTP_" prefixes in the async path (`aprocess_request`) and remove
it in Django 7. The final code in Django 7 would look like:
{{{#!diff
diff --git a/django/contrib/auth/middleware.py
b/django/contrib/auth/middleware.py
index a28e1705a3..7136a64c44 100644
--- a/django/contrib/auth/middleware.py
+++ b/django/contrib/auth/middleware.py
@@ -184,7 +184,7 @@ class RemoteUserMiddleware:
" before the RemoteUserMiddleware class."
)
try:
- username = request.META["HTTP_" + self.header]
+ username = request.META[self.header]
except KeyError:
# If specified header doesn't exist then remove any existing
# authenticated remote-user, or return (leaving request.user
set to
}}}
Regrettably, we might need a transitional setting (or class attribute) to
be able to opt into (or out of) the future, since I'm reluctant to add any
automatic checking of other forms of headers, which will just open an
avenue for security reports describing header spoofing. Open to ideas.
--
Ticket URL: <
https://code.djangoproject.com/ticket/37025>
Django <
https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.