From a32c7075cf634aee1f4f3deecd27f194097ec0c2 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Wed, 1 Apr 2026 09:36:16 -0400 Subject: [PATCH 1/7] Fixed #37017 -- Fixed setting or clearing of request.user after alogin/alogout(). Regression in 31a43c571f4d036827d4fd7a5f615591637dc1be. --- django/contrib/auth/__init__.py | 18 +++++++++++++---- docs/releases/6.0.4.txt | 5 ++++- tests/auth_tests/test_middleware.py | 31 ++++++++++++++++++++++++++++- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py index 2702c38aa4d3..21e6dc43d633 100644 --- a/django/contrib/auth/__init__.py +++ b/django/contrib/auth/__init__.py @@ -205,6 +205,8 @@ async def alogin(request, user, backend=None): await request.session.aset(SESSION_KEY, user._meta.pk.value_to_string(user)) await request.session.aset(BACKEND_SESSION_KEY, backend) await request.session.aset(HASH_SESSION_KEY, session_auth_hash) + if hasattr(request, "user"): + request.user = user if hasattr(request, "auser"): async def auser(): @@ -244,13 +246,21 @@ async def alogout(request): user = None await user_logged_out.asend(sender=user.__class__, request=request, user=user) await request.session.aflush() - if hasattr(request, "auser"): + + has_user = hasattr(request, "user") + has_auser = hasattr(request, "auser") + if has_user or has_auser: from django.contrib.auth.models import AnonymousUser - async def auser(): - return AnonymousUser() + anon = AnonymousUser() + if has_user: + request.user = anon + if has_auser: - request.auser = auser + async def auser(): + return anon + + request.auser = auser def get_user_model(): diff --git a/docs/releases/6.0.4.txt b/docs/releases/6.0.4.txt index a32a49dc93e0..e24397d9bc43 100644 --- a/docs/releases/6.0.4.txt +++ b/docs/releases/6.0.4.txt @@ -10,4 +10,7 @@ issues with severity "low", and several bugs in 6.0.3. Bugfixes ======== -* ... +* Fixed a regression in Django 6.0 where :func:`~django.contrib.auth.alogin` + and :func:`~django.contrib.auth.alogout` did not respectively set or clear + ``request.user`` if it had already been materialized (e.g., by sync + middleware) (:ticket:`37017`). diff --git a/tests/auth_tests/test_middleware.py b/tests/auth_tests/test_middleware.py index 5e106d40f731..894b49548b27 100644 --- a/tests/auth_tests/test_middleware.py +++ b/tests/auth_tests/test_middleware.py @@ -4,7 +4,7 @@ AuthenticationMiddleware, LoginRequiredMiddleware, ) -from django.contrib.auth.models import User +from django.contrib.auth.models import AnonymousUser, User from django.core.exceptions import ImproperlyConfigured from django.http import HttpRequest, HttpResponse from django.test import TestCase, modify_settings, override_settings @@ -77,6 +77,35 @@ async def test_auser_after_alogout(self): self.assertTrue(auser_second.is_anonymous) +class TestAsyncLoginLogoutAfterSyncMiddleware(TestCase): + @classmethod + def setUpTestData(cls): + cls.user = User.objects.create_user( + "test_user", "test@example.com", "test_password" + ) + cls.user2 = User.objects.create_user( + "test_user2", "test2@example.com", "test_password2" + ) + + def setUp(self): + self.middleware = AuthenticationMiddleware(lambda req: HttpResponse()) + self.client.force_login(self.user) + self.request = HttpRequest() + self.request.session = self.client.session + # Populate self.request.user. + self.middleware(self.request) + # .user is lazy, so materialize it by accessing an attribute. + self.request.user.is_authenticated + + async def test_user_after_alogin(self): + await alogin(self.request, self.user2) + self.assertEqual(self.request.user, self.user2) + + async def test_user_after_alogout(self): + await alogout(self.request) + self.assertEqual(self.request.user, AnonymousUser()) + + @override_settings(ROOT_URLCONF="auth_tests.urls") @modify_settings( MIDDLEWARE={"append": "django.contrib.auth.middleware.LoginRequiredMiddleware"} From 2ee757ee502d5663f932dc5c35175c39af4640ce Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Tue, 31 Mar 2026 15:43:18 -0400 Subject: [PATCH 2/7] Fixed #36862 -- Doc'd the need for a proxy when deploying RemoteUserMiddleware under ASGI. We have a flood of nuisance security reports describing ASGI deployments using RemoteUserMiddleware without a fronting proxy, which is not realistic. --- docs/howto/auth-remote-user.txt | 29 ++++++++++++++++++----------- docs/internals/security.txt | 6 ++++++ docs/ref/middleware.txt | 2 +- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/docs/howto/auth-remote-user.txt b/docs/howto/auth-remote-user.txt index 533f460e4ac4..fe48bb3dc86a 100644 --- a/docs/howto/auth-remote-user.txt +++ b/docs/howto/auth-remote-user.txt @@ -99,19 +99,26 @@ instead of :class:`django.contrib.auth.middleware.RemoteUserMiddleware`:: .. warning:: - Be very careful if using a ``RemoteUserMiddleware`` subclass with a custom - HTTP header. You must be sure that your front-end web server always sets or - strips that header based on the appropriate authentication checks, never - permitting an end-user to submit a fake (or "spoofed") header value. Since - the HTTP headers ``X-Auth-User`` and ``X-Auth_User`` (for example) both - normalize to the ``HTTP_X_AUTH_USER`` key in ``request.META``, you must - also check that your web server doesn't allow a spoofed header using + ``RemoteUserMiddleware`` must not be deployed in configurations where a + client can supply the header. You must be sure that your web server or + reverse proxy always sets or strips that header based on the appropriate + authentication checks, never permitting an end user to submit a fake (or + "spoofed") header value. In particular, ASGI deployments cannot be exposed + directly to the internet (that is, without a reverse proxy) when using this + middleware. + + Since the HTTP headers ``X-Auth-User`` and ``X-Auth_User`` (for example) + both normalize to the ``HTTP_X_AUTH_USER`` key in ``request.META``, you + must also check that your web server doesn't allow a spoofed header using underscores in place of dashes. - 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 - or ASGI server, not directly from an HTTP request header. + 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``. If you need more control, you can create your own authentication backend that inherits from :class:`~django.contrib.auth.backends.RemoteUserBackend` and diff --git a/docs/internals/security.txt b/docs/internals/security.txt index 5214bf0704a4..2f73a906bc54 100644 --- a/docs/internals/security.txt +++ b/docs/internals/security.txt @@ -131,6 +131,12 @@ not been sanitized:: q = MyModel.objects.extra(select={"id": query}) return HttpResponse(q.values()) +Some HTTP headers must also be sanitized by a web server or fronting proxy +before they can be used, such as ``Remote-User`` and ``X-Forwarded-*``. For +instance, under ASGI, it is a deployment misconfiguration (rather than any flaw +in Django) for Django to be the direct HTTP endpoint when +:class:`~django.contrib.auth.middleware.RemoteUserMiddleware` is used. + Request headers and URLs must be under 8K bytes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/ref/middleware.txt b/docs/ref/middleware.txt index a9638cf36e5c..692001ddf8ba 100644 --- a/docs/ref/middleware.txt +++ b/docs/ref/middleware.txt @@ -582,7 +582,7 @@ Customize the login URL or field name for authenticated views with the Middleware for utilizing web server provided authentication when enabled only on the login page. See :ref:`persistent-remote-user-middleware-howto` - for usage details. + for usage details, including security considerations. CSRF protection middleware -------------------------- From 90cd510b3b033605907f6521ef98f35d2bd6c3a0 Mon Sep 17 00:00:00 2001 From: Natalia <124304+nessita@users.noreply.github.com> Date: Mon, 30 Mar 2026 16:59:30 -0300 Subject: [PATCH 3/7] Added section for respecting maintainer time to the security policy. This follows a post from Seth Larson (Security Developer-in-Residence at the PSF): https://sethmlarson.dev/respecting-maintainer-time-should-be-in-security-policies --- docs/internals/security.txt | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/docs/internals/security.txt b/docs/internals/security.txt index 2f73a906bc54..3dd811fcd0ee 100644 --- a/docs/internals/security.txt +++ b/docs/internals/security.txt @@ -43,6 +43,39 @@ the industry-standard 90 days. Confirmed vulnerabilities with a .. _our public Trac instance: https://code.djangoproject.com/query +.. _respecting-maintainer-time: + +Respecting maintainer time +-------------------------- + +Django's security team are volunteers. Please be mindful and respectful of +their time when submitting reports. Your initial report should give the team +enough to make a triage decision, no more. It should include: + +* A brief description of the issue and where in Django it occurs. + +* A minimal, working proof of concept (code snippet or reproduction steps). + +* The versions of Django and Python you tested against. + +* Optionally, a minimal patch with the mitigation for the issue. + +Please do not include severity scores (CVSS or otherwise), lengthy background +sections, multiple headers, or a determination of whether the issue constitutes +a vulnerability. The security team will make those assessments. Extensive +upfront analysis makes triage slower, not faster. If the team confirms the +issue is a valid vulnerability, they will follow up and welcome further detail +at that stage. + +If you have identified multiple potential issues, please wait for a triage +result on your initial report before submitting further ones. Exceptions can be +made for issues that are clearly and directly related to an already reported +finding. Feedback on an initial report is often relevant to subsequent ones, +and taking the time to read and incorporate it leads to better reports overall. + +The security team is not able to process large volumes of reports submitted in +a short period of time, and reports submitted in bulk may be put on hold. + Reporting guidelines -------------------- From 253f552c5809fa096116b601bd842ca4f3504860 Mon Sep 17 00:00:00 2001 From: David Smith Date: Mon, 30 Mar 2026 23:16:32 +0100 Subject: [PATCH 4/7] Fixed #36949 -- Improved RelatedFieldWidgetWrapper