1
0
mirror of https://github.com/django/django.git synced 2025-10-28 16:16:12 +00:00

Fixed #14445 - Use HMAC and constant-time comparison functions where needed.

All adhoc MAC applications have been updated to use HMAC, using SHA1 to
generate unique keys for each application based on the SECRET_KEY, which is
common practice for this situation. In all cases, backwards compatibility
with existing hashes has been maintained, aiming to phase this out as per
the normal deprecation process. In this way, under most normal
circumstances the old hashes will have expired (e.g. by session expiration
etc.) before they become invalid.

In the case of the messages framework and the cookie backend, which was
already using HMAC, there is the possibility of a backwards incompatibility
if the SECRET_KEY is shorter than the default 50 bytes, but the low
likelihood and low impact meant compatibility code was not worth it.

All known instances where tokens/hashes were compared using simple string
equality, which could potentially open timing based attacks, have also been
fixed using a constant-time comparison function.

There are no known practical attacks against the existing implementations,
so these security improvements will not be backported.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14218 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Luke Plant
2010-10-14 20:54:30 +00:00
parent 36f2f7ee7c
commit 45c7f427ce
18 changed files with 476 additions and 51 deletions

View File

@@ -1,6 +1,9 @@
from datetime import date
from django.conf import settings
from django.utils.hashcompat import sha_constructor
from django.utils.http import int_to_base36, base36_to_int
from django.utils.crypto import constant_time_compare, salted_hmac
class PasswordResetTokenGenerator(object):
"""
@@ -30,8 +33,12 @@ class PasswordResetTokenGenerator(object):
return False
# Check that the timestamp/uid has not been tampered with
if self._make_token_with_timestamp(user, ts) != token:
return False
if not constant_time_compare(self._make_token_with_timestamp(user, ts), token):
# Fallback to Django 1.2 method for compatibility.
# PendingDeprecationWarning <- here to remind us to remove this in
# Django 1.5
if not constant_time_compare(self._make_token_with_timestamp_old(user, ts), token):
return False
# Check the timestamp is within limit
if (self._num_days(self._today()) - ts) > settings.PASSWORD_RESET_TIMEOUT_DAYS:
@@ -50,7 +57,16 @@ class PasswordResetTokenGenerator(object):
# last_login will also change), we produce a hash that will be
# invalid as soon as it is used.
# We limit the hash to 20 chars to keep URL short
from django.utils.hashcompat import sha_constructor
key_salt = "django.contrib.auth.tokens.PasswordResetTokenGenerator"
value = unicode(user.id) + \
user.password + user.last_login.strftime('%Y-%m-%d %H:%M:%S') + \
unicode(timestamp)
hash = salted_hmac(key_salt, value).hexdigest()[::2]
return "%s-%s" % (ts_b36, hash)
def _make_token_with_timestamp_old(self, user, timestamp):
# The Django 1.2 method
ts_b36 = int_to_base36(timestamp)
hash = sha_constructor(settings.SECRET_KEY + unicode(user.id) +
user.password + user.last_login.strftime('%Y-%m-%d %H:%M:%S') +
unicode(timestamp)).hexdigest()[::2]