Fixed CVE-2018-16984 -- Fixed password hash disclosure to admin "view only" users.
Thanks Claude Paroz & Tim Graham for collaborating on the patch. # Conflicts: # tests/auth_tests/test_views.py
This commit is contained in:
parent
f86100ab18
commit
c4bd5b597e
|
@ -202,6 +202,12 @@ class AdminReadonlyField:
|
||||||
except (AttributeError, ValueError, ObjectDoesNotExist):
|
except (AttributeError, ValueError, ObjectDoesNotExist):
|
||||||
result_repr = self.empty_value_display
|
result_repr = self.empty_value_display
|
||||||
else:
|
else:
|
||||||
|
if field in self.form.fields:
|
||||||
|
widget = self.form[field].field.widget
|
||||||
|
# This isn't elegant but suffices for contrib.auth's
|
||||||
|
# ReadOnlyPasswordHashWidget.
|
||||||
|
if getattr(widget, 'read_only', False):
|
||||||
|
return widget.render(field, value)
|
||||||
if f is None:
|
if f is None:
|
||||||
if getattr(attr, 'boolean', False):
|
if getattr(attr, 'boolean', False):
|
||||||
result_repr = _boolean_icon(value)
|
result_repr = _boolean_icon(value)
|
||||||
|
|
|
@ -22,6 +22,7 @@ UserModel = get_user_model()
|
||||||
|
|
||||||
class ReadOnlyPasswordHashWidget(forms.Widget):
|
class ReadOnlyPasswordHashWidget(forms.Widget):
|
||||||
template_name = 'auth/widgets/read_only_password_hash.html'
|
template_name = 'auth/widgets/read_only_password_hash.html'
|
||||||
|
read_only = True
|
||||||
|
|
||||||
def get_context(self, name, value, attrs):
|
def get_context(self, name, value, attrs):
|
||||||
context = super().get_context(name, value, attrs)
|
context = super().get_context(name, value, attrs)
|
||||||
|
|
|
@ -4,8 +4,17 @@ Django 2.1.2 release notes
|
||||||
|
|
||||||
*Expected October 1, 2018*
|
*Expected October 1, 2018*
|
||||||
|
|
||||||
Django 2.1.2 fixes several bugs in 2.1.1. Also, the latest string translations
|
Django 2.1.2 fixes a security issue and several bugs in 2.1.1. Also, the latest
|
||||||
from Transifex are incorporated.
|
string translations from Transifex are incorporated.
|
||||||
|
|
||||||
|
CVE-2018-16984: Password hash disclosure to "view only" admin users
|
||||||
|
===================================================================
|
||||||
|
|
||||||
|
If an admin user has the change permission to the user model, only part of the
|
||||||
|
password hash is displayed in the change form. Admin users with the view (but
|
||||||
|
not change) permission to the user model were displayed the entire hash. While
|
||||||
|
it's typically infeasible to reverse a strong password hash, if your site uses
|
||||||
|
weaker password hashing algorithms such as MD5 or SHA1, it could be a problem.
|
||||||
|
|
||||||
Bugfixes
|
Bugfixes
|
||||||
========
|
========
|
||||||
|
|
|
@ -14,11 +14,12 @@ from django.contrib.auth import (
|
||||||
from django.contrib.auth.forms import (
|
from django.contrib.auth.forms import (
|
||||||
AuthenticationForm, PasswordChangeForm, SetPasswordForm,
|
AuthenticationForm, PasswordChangeForm, SetPasswordForm,
|
||||||
)
|
)
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import Permission, User
|
||||||
from django.contrib.auth.views import (
|
from django.contrib.auth.views import (
|
||||||
INTERNAL_RESET_SESSION_TOKEN, LoginView, logout_then_login,
|
INTERNAL_RESET_SESSION_TOKEN, LoginView, logout_then_login,
|
||||||
redirect_to_login,
|
redirect_to_login,
|
||||||
)
|
)
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.sessions.middleware import SessionMiddleware
|
from django.contrib.sessions.middleware import SessionMiddleware
|
||||||
from django.contrib.sites.requests import RequestSite
|
from django.contrib.sites.requests import RequestSite
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
|
@ -1115,6 +1116,11 @@ class LogoutTest(AuthViewsTestCase):
|
||||||
self.assertRedirects(response, '/logout/', fetch_redirect_response=False)
|
self.assertRedirects(response, '/logout/', fetch_redirect_response=False)
|
||||||
|
|
||||||
|
|
||||||
|
def get_perm(Model, perm):
|
||||||
|
ct = ContentType.objects.get_for_model(Model)
|
||||||
|
return Permission.objects.get(content_type=ct, codename=perm)
|
||||||
|
|
||||||
|
|
||||||
# Redirect in test_user_change_password will fail if session auth hash
|
# Redirect in test_user_change_password will fail if session auth hash
|
||||||
# isn't updated after password change (#21649)
|
# isn't updated after password change (#21649)
|
||||||
@override_settings(ROOT_URLCONF='auth_tests.urls_admin')
|
@override_settings(ROOT_URLCONF='auth_tests.urls_admin')
|
||||||
|
@ -1221,6 +1227,25 @@ class ChangelistTests(AuthViewsTestCase):
|
||||||
response = self.client.get(reverse('auth_test_admin:auth_user_password_change', args=('foobar',)))
|
response = self.client.get(reverse('auth_test_admin:auth_user_password_change', args=('foobar',)))
|
||||||
self.assertEqual(response.status_code, 404)
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
|
def test_view_user_password_is_readonly(self):
|
||||||
|
u = User.objects.get(username='testclient')
|
||||||
|
u.is_superuser = False
|
||||||
|
u.save()
|
||||||
|
u.user_permissions.add(get_perm(User, 'view_user'))
|
||||||
|
response = self.client.get(reverse('auth_test_admin:auth_user_change', args=(u.pk,)),)
|
||||||
|
algo, salt, hash_string = (u.password.split('$'))
|
||||||
|
self.assertContains(response, '<div class="readonly">testclient</div>')
|
||||||
|
# ReadOnlyPasswordHashWidget is used to render the field.
|
||||||
|
self.assertContains(
|
||||||
|
response,
|
||||||
|
'<strong>algorithm</strong>: %s\n\n'
|
||||||
|
'<strong>salt</strong>: %s**********\n\n'
|
||||||
|
'<strong>hash</strong>: %s**************************\n\n' % (
|
||||||
|
algo, salt[:2], hash_string[:6],
|
||||||
|
),
|
||||||
|
html=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@override_settings(
|
@override_settings(
|
||||||
AUTH_USER_MODEL='auth_tests.UUIDUser',
|
AUTH_USER_MODEL='auth_tests.UUIDUser',
|
||||||
|
|
Loading…
Reference in New Issue