diff --git a/swift/common/middleware/formpost.py b/swift/common/middleware/formpost.py index 80d903b6bc..f13fcaa6a1 100644 --- a/swift/common/middleware/formpost.py +++ b/swift/common/middleware/formpost.py @@ -110,7 +110,7 @@ from time import time from urllib import quote, unquote -from swift.common.utils import get_logger +from swift.common.utils import get_logger, strcmp_const_time #: The size of data to read from the form at any given time. @@ -442,7 +442,8 @@ def _perform_subrequest(self, env, start_response, attributes, fp, key): attributes.get('expires') or '0' ) sig = hmac.new(key, hmac_body, sha1).hexdigest() - if sig != (attributes.get('signature') or 'invalid'): + if not strcmp_const_time(sig,(attributes.get('signature') or + 'invalid')): return '401 Unauthorized', 'invalid signature' subenv['swift.authorize'] = lambda req: None subenv['swift.authorize_override'] = True diff --git a/swift/common/utils.py b/swift/common/utils.py index 2708a789d9..310969609e 100644 --- a/swift/common/utils.py +++ b/swift/common/utils.py @@ -1117,3 +1117,23 @@ def listdir(path): if err.errno != errno.ENOENT: raise return [] + + +def strcmp_const_time(s1, s2): + """Constant-time string comparison. + + :params s1: the first string + :params s2: the second string + + :return: True if the strings are equal. + + This function takes two strings and compares them. It is intended to be + used when doing a comparison for authentication purposes to help guard + against timing attacks. + """ + if len(s1) != len(s2): + return False + result = 0 + for (a, b) in zip(s1, s2): + result |= ord(a) ^ ord(b) + return result == 0 diff --git a/test/unit/common/test_utils.py b/test/unit/common/test_utils.py index 7de27541ad..290f6824e6 100644 --- a/test/unit/common/test_utils.py +++ b/test/unit/common/test_utils.py @@ -850,6 +850,11 @@ def test_TRUE_VALUES(self): for v in utils.TRUE_VALUES: self.assertEquals(v, v.lower()) + def test_strcmp_const_time(self): + self.assertTrue(utils.strcmp_const_time('abc123', 'abc123')) + self.assertFalse(utils.strcmp_const_time('a', 'aaaaa')) + self.assertFalse(utils.strcmp_const_time('ABC123', 'abc123')) + if __name__ == '__main__': unittest.main()