From 034987f083cfa1aff2347a4f644ccfb72a46dae9 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Tue, 15 Jul 2014 12:41:48 -0700 Subject: [PATCH] Use compare_digest or an equivalent when available This avoids the same issue that caused a prior exploit in nova, we might as well avoid this by doing it correctly in the first place. See: https://bugs.launchpad.net/bugs/cve/2014-3517 Change-Id: Iea4b3bcded268de926f32f956383d03fd9f15d59 --- osprofiler/_utils.py | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/osprofiler/_utils.py b/osprofiler/_utils.py index d691a03..81d9cd7 100644 --- a/osprofiler/_utils.py +++ b/osprofiler/_utils.py @@ -21,6 +21,30 @@ import os import six +try: + # Only in python 2.7.7+ (and python 3.3+) + # https://docs.python.org/2/library/hmac.html#hmac.compare_digest + from hmac import compare_digest # noqa +except (AttributeError, ImportError): + # Taken/slightly modified from: + # https://mail.python.org/pipermail/python-checkins/2012-June/114532.html + def compare_digest(a, b): + """Returns the equivalent of 'a == b', but avoids content based short + circuiting to reduce the vulnerability to timing attacks. + """ + # We assume the length of the expected digest is public knowledge, + # thus this early return isn't leaking anything an attacker wouldn't + # already know + if len(a) != len(b): + return False + + # We assume that integers in the bytes range are all cached, + # thus timing shouldn't vary much due to integer object creation + result = 0 + for x, y in zip(a, b): + result |= ord(x) ^ ord(y) + return result == 0 + def binary_encode(text, encoding='utf-8'): """Converts a string of into a binary type using given encoding. @@ -84,10 +108,11 @@ def signed_unpack(data, hmac_data, hmac_key): if not (hmac_key and hmac_data): return None + hmac_data = hmac_data.strip() try: - if generate_hmac(data, hmac_key) != hmac_data.strip(): + user_hmac_data = generate_hmac(data, hmac_key) + if not compare_digest(hmac_data, user_hmac_data): return None - return json.loads(binary_decode(base64.urlsafe_b64decode(data))) except Exception: return None