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:  
    184184                " before the RemoteUserMiddleware class."
    185185            )
    186186        try:
    187             username = request.META["HTTP_" + self.header]
     187            username = request.META[self.header]
    188188        except KeyError:
    189189            # If specified header doesn't exist then remove any existing
    190190            # 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.

Change History (1)

comment:1 by Jacob Walls, 3 hours ago

Owner: set to Jacob Walls
Status: newassigned
Note: See TracTickets for help on using tickets.
Back to Top