Opened 3 hours ago
Last modified 3 hours ago
#37025 assigned Bug
Deprecate the prefixing of HTTP_ to header names in RemoteUserMiddleware.aprocess_request()
| Reported by: | Jacob Walls | Owned by: | Jacob Walls |
|---|---|---|---|
| Component: | contrib.auth | Version: | 5.2 |
| Severity: | Normal | Keywords: | |
| Cc: | Jon Janzen | Triage Stage: | Unreviewed |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
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:
-
django/contrib/auth/middleware.py
diff --git a/django/contrib/auth/middleware.py b/django/contrib/auth/middleware.py index a28e1705a3..7136a64c44 100644
a b class RemoteUserMiddleware: 184 184 " before the RemoteUserMiddleware class." 185 185 ) 186 186 try: 187 username = request.META[ "HTTP_" +self.header]187 username = request.META[self.header] 188 188 except KeyError: 189 189 # If specified header doesn't exist then remove any existing 190 190 # 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.