diff --git a/django/contrib/auth/backends.py b/django/contrib/auth/backends.py
index 6b31f72..4ee326b 100644
a
|
b
|
class ModelBackend(object):
|
10 | 10 | |
11 | 11 | def authenticate(self, username=None, password=None, **kwargs): |
12 | 12 | UserModel = get_user_model() |
| 13 | |
| 14 | user = UserModel() |
| 15 | |
13 | 16 | if username is None: |
14 | 17 | username = kwargs.get(UserModel.USERNAME_FIELD) |
15 | 18 | try: |
… |
… |
class ModelBackend(object):
|
17 | 20 | if user.check_password(password): |
18 | 21 | return user |
19 | 22 | except UserModel.DoesNotExist: |
| 23 | #This call to set password is used to force the hasher to run. |
| 24 | #It helps to even out the timing between a successful and |
| 25 | #unsuccessful authentication attempt, helping to mitigate a timing |
| 26 | #vuln. |
| 27 | user.set_password(password) |
20 | 28 | return None |
21 | 29 | |
22 | 30 | def get_group_permissions(self, user_obj, obj=None): |
diff --git a/django/contrib/auth/tests/test_auth_backends.py b/django/contrib/auth/tests/test_auth_backends.py
index fc5a80e..aa69220 100644
a
|
b
|
from django.contrib.auth import authenticate, get_user
|
12 | 12 | from django.http import HttpRequest |
13 | 13 | from django.test import TestCase |
14 | 14 | from django.test.utils import override_settings |
| 15 | from django.contrib.auth.hashers import MD5PasswordHasher |
15 | 16 | |
| 17 | """ |
| 18 | Mock hasher which counts number of times hashes are computed |
| 19 | |
| 20 | Used in test_authentication_timing |
| 21 | |
| 22 | Refs #20760 |
| 23 | """ |
| 24 | class MockHasher(MD5PasswordHasher): |
| 25 | def __init__(self): |
| 26 | MockHasher.hash_count = 0 |
| 27 | |
| 28 | def encode(self, password, salt, iterations=None): |
| 29 | MockHasher.hash_count = MockHasher.hash_count + 1 |
| 30 | return super(MockHasher, self).encode(password, salt, iterations) |
16 | 31 | |
17 | 32 | class BaseModelBackendTest(object): |
18 | 33 | """ |
… |
… |
class BaseModelBackendTest(object):
|
111 | 126 | user = self.UserModel._default_manager.get(pk=self.superuser.pk) |
112 | 127 | self.assertEqual(len(user.get_all_permissions()), len(Permission.objects.all())) |
113 | 128 | |
| 129 | @override_settings(PASSWORD_HASHERS=('django.contrib.auth.tests.test_auth_backends.MockHasher',)) |
| 130 | def test_authentication_timing(self): |
| 131 | """ |
| 132 | Tests that the hasher is run the same number of times whether a user exists or not |
| 133 | |
| 134 | Refs #20760 |
| 135 | """ |
| 136 | #ensures the proper natural key is passed in custom models |
| 137 | kwargs = {self.UserModel.USERNAME_FIELD : self.UserModel._default_manager.get(pk=1).natural_key()[0]} |
| 138 | authenticate(password='test', **kwargs) |
| 139 | exists = MockHasher.hash_count |
| 140 | |
| 141 | MockHasher.hash_count = 0 |
| 142 | |
| 143 | #don't use these creds in a test account |
| 144 | kwargs = {self.UserModel.USERNAME_FIELD : 'paglierani'} |
| 145 | authenticate(password='justin', **kwargs) |
| 146 | dne = MockHasher.hash_count |
| 147 | self.assertEqual(exists, dne) #counts should be the same |
| 148 | |
114 | 149 | |
115 | 150 | @skipIfCustomUser |
116 | 151 | class ModelBackendTest(BaseModelBackendTest, TestCase): |