diff --git a/flask_httpauth.py b/flask_httpauth.py index ee35eca..86297c4 100644 --- a/flask_httpauth.py +++ b/flask_httpauth.py @@ -13,22 +13,11 @@ from random import Random, SystemRandom from flask import request, make_response, session from werkzeug.datastructures import Authorization +from werkzeug.security import safe_str_cmp __version__ = '3.2.4' -try: - from hmac import compare_digest -except ImportError: - import operator - - # This fallback is not constant-time, but making it constant-time - # introduces additional complexity. For full-secure password - # comparison, applications should use Python 2 >= 2.7.7 or - # Python 3 >= 3.4, or use hashed passwords. - compare_digest = operator.eq - - class HTTPAuth(object): def __init__(self, scheme=None, realm=None): self.scheme = scheme @@ -154,18 +143,9 @@ def authenticate(self, auth, stored_password): except TypeError: client_password = self.hash_password_callback(username, client_password) - if stored_password is None or client_password is None: - return False - - try: - stored_password = stored_password.encode("utf-8") - except AttributeError: - pass - try: - client_password = client_password.encode("utf-8") - except AttributeError: - pass - return compare_digest(client_password, stored_password) + return client_password is not None and \ + stored_password is not None and \ + safe_str_cmp(client_password, stored_password) class HTTPDigestAuth(HTTPAuth): @@ -194,14 +174,17 @@ def default_verify_nonce(nonce): session_nonce = session.get("auth_nonce") if nonce is None or session_nonce is None: return False - return compare_digest(nonce, session_nonce) + return safe_str_cmp(nonce, session_nonce) def default_generate_opaque(): session["auth_opaque"] = _generate_random() return session["auth_opaque"] def default_verify_opaque(opaque): - return opaque == session.get("auth_opaque") + session_opaque = session.get("auth_opaque") + if opaque is None or session_opaque is None: + return False + return safe_str_cmp(opaque, session_opaque) self.generate_nonce(default_generate_nonce) self.generate_opaque(default_generate_opaque) @@ -260,7 +243,7 @@ def authenticate(self, auth, stored_password_or_ha1): ha2 = md5(a2.encode('utf-8')).hexdigest() a3 = ha1 + ":" + auth.nonce + ":" + ha2 response = md5(a3.encode('utf-8')).hexdigest() - return compare_digest(response, auth.response) + return safe_str_cmp(response, auth.response) class HTTPTokenAuth(HTTPAuth):