diff --git a/r2/r2/lib/utils/utils.py b/r2/r2/lib/utils/utils.py index 482d459c9..dc1712c57 100644 --- a/r2/r2/lib/utils/utils.py +++ b/r2/r2/lib/utils/utils.py @@ -1342,3 +1342,19 @@ def thread_dump(*a): % dict(filename=filename, lineno=lineno, fnname=fnname)) sys.stderr.write('\t\t\t%(line)s\n' % dict(line=line)) + +def constant_time_compare(actual, expected): + """ + Returns True if the two strings are equal, False otherwise + + The time taken is dependent on the number of charaters provided + instead of the number of characters that match. + """ + actual_len = len(actual) + expected_len = len(expected) + result = actual_len ^ expected_len + if expected_len > 0: + for i in xrange(actual_len): + result |= ord(actual[i]) ^ ord(expected[i % expected_len]) + return result == 0 + diff --git a/r2/r2/models/account.py b/r2/r2/models/account.py index 99297870b..f1a4e9452 100644 --- a/r2/r2/models/account.py +++ b/r2/r2/models/account.py @@ -25,6 +25,7 @@ from r2.lib.db.userrel import UserRel from r2.lib.memoize import memoize from r2.lib.utils import modhash, valid_hash, randstr, timefromnow from r2.lib.utils import UrlParser, set_last_visit, last_visit +from r2.lib.utils import constant_time_compare from r2.lib.cache import sgm from r2.lib.log import log_text @@ -550,9 +551,9 @@ def valid_cookie(cookie): except NotFound: return (False, False) - if cookie == account.make_cookie(timestr, admin = False): + if constant_time_compare(cookie, account.make_cookie(timestr, admin = False)): return (account, False) - elif cookie == account.make_cookie(timestr, admin = True): + elif constant_time_compare(cookie, account.make_cookie(timestr, admin = True)): return (account, True) return (False, False) @@ -563,7 +564,7 @@ def valid_feed(name, feedhash, path): try: user = Account._by_name(name) if (user.pref_private_feeds and - feedhash == make_feedhash(user, path)): + constant_time_compare(feedhash, make_feedhash(user, path))): return user except NotFound: pass @@ -590,17 +591,21 @@ def valid_login(name, password): def valid_password(a, password): try: - if a.password == passhash(a.name, password, ''): + # A constant_time_compare isn't strictly required here + # but it is doesn't hurt + if constant_time_compare(a.password, passhash(a.name, password, '')): #add a salt a.password = passhash(a.name, password, True) a._commit() return a else: salt = a.password[:3] - if a.password == passhash(a.name, password, salt): + if constant_time_compare(a.password, passhash(a.name, password, salt)): return a except AttributeError, UnicodeEncodeError: return False + # Python defaults to returning None + return False def passhash(username, password, salt = ''): if salt is True: