diff options
| author | Jake Howard <git@theorangeone.net> | 2025-11-19 16:52:28 +0000 |
|---|---|---|
| committer | Jacob Walls <jacobtylerwalls@gmail.com> | 2026-02-03 07:59:11 -0500 |
| commit | d72cc3be3be0bbebdcaea5a8c8106b4d6f2a32bd (patch) | |
| tree | 7d6c6bc141ac431b0dd65ec0131c0c72beac9dbc /django | |
| parent | d64ef2429f43cc69a9d5f491650734bb3c51c21d (diff) | |
[6.0.x] Fixed CVE-2025-13473 -- Standardized timing of check_password() in mod_wsgi auth handler.
Refs CVE-2024-39329, #20760.
Thanks Stackered for the report, and Jacob Walls and Markus Holtermann
for the reviews.
Co-authored-by: Natalia <124304+nessita@users.noreply.github.com>
Backport of 3eb814e02a4c336866d4189fa0c24fd1875863ed from main.
Diffstat (limited to 'django')
| -rw-r--r-- | django/contrib/auth/handlers/modwsgi.py | 37 |
1 files changed, 30 insertions, 7 deletions
diff --git a/django/contrib/auth/handlers/modwsgi.py b/django/contrib/auth/handlers/modwsgi.py index 591ec72cb4..086db89fc8 100644 --- a/django/contrib/auth/handlers/modwsgi.py +++ b/django/contrib/auth/handlers/modwsgi.py @@ -4,24 +4,47 @@ from django.contrib import auth UserModel = auth.get_user_model() +def _get_user(username): + """ + Return the UserModel instance for `username`. + + If no matching user exists, or if the user is inactive, return None, in + which case the default password hasher is run to mitigate timing attacks. + """ + try: + user = UserModel._default_manager.get_by_natural_key(username) + except UserModel.DoesNotExist: + user = None + else: + if not user.is_active: + user = None + + if user is None: + # Run the default password hasher once to reduce the timing difference + # between existing/active and nonexistent/inactive users (#20760). + UserModel().set_password("") + + return user + + def check_password(environ, username, password): """ Authenticate against Django's auth database. mod_wsgi docs specify None, True, False as return value depending on whether the user exists and authenticates. + + Return None if the user does not exist, return False if the user exists but + password is not correct, and return True otherwise. + """ # db connection state is managed similarly to the wsgi handler # as mod_wsgi may call these functions outside of a request/response cycle db.reset_queries() try: - try: - user = UserModel._default_manager.get_by_natural_key(username) - except UserModel.DoesNotExist: - return None - if not user.is_active: - return None - return user.check_password(password) + user = _get_user(username) + if user: + return user.check_password(password) finally: db.close_old_connections() |
