summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorJake Howard <git@theorangeone.net>2025-11-19 16:52:28 +0000
committerJacob Walls <jacobtylerwalls@gmail.com>2026-02-03 08:24:15 -0500
commit6dc23508f3395e1254c315084c7334ef81c4c09a (patch)
tree09f7c9548f83a3b0996b0acd4a0113834675e6e6 /django
parenta28c3c739564ccc2aabc7b20211f54d838c7b582 (diff)
[4.2.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.py37
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()