Authentication: Support for fallback secret keys in get_session_auth_hash (#4481)

* Authentication: Support for fallback secret keys in get_session_auth_hash

* Update src/pretix/presale/utils.py

Co-authored-by: Richard Schreiber <schreiber@rami.io>

---------

Co-authored-by: Richard Schreiber <schreiber@rami.io>
This commit is contained in:
Raphael Michel
2024-10-07 16:58:37 +02:00
committed by GitHub
parent cdc5401dc2
commit 6cc9529d9a
4 changed files with 52 additions and 5 deletions

View File

@@ -571,13 +571,23 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin):
def get_session_auth_hash(self):
"""
Return an HMAC that needs to
Return an HMAC that needs to be the same throughout the session, used e.g. for forced
logout after every password change.
"""
return self._get_session_auth_hash(secret=settings.SECRET_KEY)
def get_session_auth_fallback_hash(self):
for fallback_secret in settings.SECRET_KEY_FALLBACKS:
yield self._get_session_auth_hash(secret=fallback_secret)
def _get_session_auth_hash(self, secret):
"""
"""
key_salt = "pretix.base.models.User.get_session_auth_hash"
payload = self.password
payload += self.email
payload += self.session_token
return salted_hmac(key_salt, payload).hexdigest()
return salted_hmac(key_salt, payload, secret=secret).hexdigest()
def update_session_token(self):
self.session_token = generate_session_token()

View File

@@ -219,13 +219,24 @@ class Customer(LoggedModel):
return is_password_usable(self.password)
def get_session_auth_hash(self):
"""
Return an HMAC that needs to be the same throughout the session, used e.g. for forced
logout after every password change.
"""
return self._get_session_auth_hash(secret=settings.SECRET_KEY)
def get_session_auth_fallback_hash(self):
for fallback_secret in settings.SECRET_KEY_FALLBACKS:
yield self._get_session_auth_hash(secret=fallback_secret)
def _get_session_auth_hash(self, secret):
"""
Return an HMAC of the password field.
"""
key_salt = "pretix.base.models.customers.Customer.get_session_auth_hash"
payload = self.password
payload += self.email
return salted_hmac(key_salt, payload).hexdigest()
return salted_hmac(key_salt, payload, secret=secret).hexdigest()
def get_email_context(self):
from pretix.base.settings import get_name_parts_localized

View File

@@ -100,10 +100,23 @@ def get_customer(request):
request._cached_customer = None
else:
session_hash = session.get(hash_session_key)
session_auth_hash = customer.get_session_auth_hash()
session_hash_verified = session_hash and constant_time_compare(
session_hash,
customer.get_session_auth_hash()
session_auth_hash,
)
if not session_hash_verified:
# If the current secret does not verify the session, try
# with the fallback secrets and stop when a matching one is
# found.
if session_hash and any(
constant_time_compare(session_hash, fallback_auth_hash)
for fallback_auth_hash in customer.get_session_auth_fallback_hash()
):
request.session.cycle_key()
request.session[hash_session_key] = session_auth_hash
session_hash_verified = True
if session_hash_verified:
request._cached_customer = customer
else: