From eb286aa22f2a83a091b856ab2ecda1bded4203e3 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Wed, 6 Jun 2012 10:53:16 +0200 Subject: [PATCH] Delayed encoding of password and salt in password checking. Applied the rule that string encoding should happen as late as possible. This is also a preparation for Python 3 compatibility. --- django/contrib/auth/forms.py | 3 --- django/contrib/auth/hashers.py | 13 ++++--------- django/utils/crypto.py | 3 +++ docs/releases/1.5.txt | 9 +++++++++ 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py index f0ef124b20..deb6cb9414 100644 --- a/django/contrib/auth/forms.py +++ b/django/contrib/auth/forms.py @@ -1,7 +1,6 @@ from django import forms from django.forms.util import flatatt from django.template import loader -from django.utils.encoding import smart_str from django.utils.http import int_to_base36 from django.utils.safestring import mark_safe from django.utils.translation import ugettext, ugettext_lazy as _ @@ -26,8 +25,6 @@ class ReadOnlyPasswordHashWidget(forms.Widget): final_attrs = self.build_attrs(attrs) - encoded = smart_str(encoded) - if len(encoded) == 32 and '$' not in encoded: algorithm = 'unsalted_md5' else: diff --git a/django/contrib/auth/hashers.py b/django/contrib/auth/hashers.py index 52d204cce6..11a6313ed7 100644 --- a/django/contrib/auth/hashers.py +++ b/django/contrib/auth/hashers.py @@ -40,9 +40,6 @@ def check_password(password, encoded, setter=None, preferred='default'): return False preferred = get_hasher(preferred) - raw_password = password - password = smart_str(password) - encoded = smart_str(encoded) if len(encoded) == 32 and '$' not in encoded: hasher = get_hasher('unsalted_md5') @@ -53,7 +50,7 @@ def check_password(password, encoded, setter=None, preferred='default'): must_update = hasher.algorithm != preferred.algorithm is_correct = hasher.verify(password, encoded) if setter and is_correct and must_update: - setter(raw_password) + setter(password) return is_correct @@ -69,11 +66,9 @@ def make_password(password, salt=None, hasher='default'): return UNUSABLE_PASSWORD hasher = get_hasher(hasher) - password = smart_str(password) if not salt: salt = hasher.salt() - salt = smart_str(salt) return hasher.encode(password, salt) @@ -291,7 +286,7 @@ class SHA1PasswordHasher(BasePasswordHasher): def encode(self, password, salt): assert password assert salt and '$' not in salt - hash = hashlib.sha1(salt + password).hexdigest() + hash = hashlib.sha1(smart_str(salt + password)).hexdigest() return "%s$%s$%s" % (self.algorithm, salt, hash) def verify(self, password, encoded): @@ -319,7 +314,7 @@ class MD5PasswordHasher(BasePasswordHasher): def encode(self, password, salt): assert password assert salt and '$' not in salt - hash = hashlib.md5(salt + password).hexdigest() + hash = hashlib.md5(smart_str(salt + password)).hexdigest() return "%s$%s$%s" % (self.algorithm, salt, hash) def verify(self, password, encoded): @@ -353,7 +348,7 @@ class UnsaltedMD5PasswordHasher(BasePasswordHasher): return '' def encode(self, password, salt): - return hashlib.md5(password).hexdigest() + return hashlib.md5(smart_str(password)).hexdigest() def verify(self, password, encoded): encoded_2 = self.encode(password, '') diff --git a/django/utils/crypto.py b/django/utils/crypto.py index 9d6486c601..0fce06012b 100644 --- a/django/utils/crypto.py +++ b/django/utils/crypto.py @@ -22,6 +22,7 @@ except NotImplementedError: using_sysrandom = False from django.conf import settings +from django.utils.encoding import smart_str _trans_5c = b"".join([chr(x ^ 0x5C) for x in xrange(256)]) @@ -137,6 +138,8 @@ def pbkdf2(password, salt, iterations, dklen=0, digest=None): assert iterations > 0 if not digest: digest = hashlib.sha256 + password = smart_str(password) + salt = smart_str(salt) hlen = digest().digest_size if not dklen: dklen = hlen diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index 0d86a52670..696f332285 100644 --- a/docs/releases/1.5.txt +++ b/docs/releases/1.5.txt @@ -128,6 +128,15 @@ If you were using the ``data`` parameter in a PUT request without a ``content_type``, you must encode your data before passing it to the test client and set the ``content_type`` argument. +String types of hasher method parameters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you have written a :ref:`custom password hasher `, +your ``encode()``, ``verify()`` or ``safe_summary()`` methods should accept +Unicode parameters (``password``, ``salt`` or ``encoded``). If any of the +hashing methods need byte strings, you can use the +:func:`~django.utils.encoding.smart_str` utility to encode the strings. + Features deprecated in 1.5 ==========================