diff --git a/CTFd/utils/sessions/__init__.py b/CTFd/utils/sessions/__init__.py index d6e28203..0b687fa1 100644 --- a/CTFd/utils/sessions/__init__.py +++ b/CTFd/utils/sessions/__init__.py @@ -51,7 +51,12 @@ class CachingSessionInterface(SessionInterface): session_class = CachedSession def _generate_sid(self): - return str(uuid4()) + sid = str(uuid4()) + v = cache.get(key=self.key_prefix + sid) + while v: + sid = str(uuid4()) + v = cache.get(key=self.key_prefix + sid) + return sid def __init__(self, key_prefix, use_signer=True, permanent=False): self.key_prefix = key_prefix diff --git a/tests/utils/test_sessions.py b/tests/utils/test_sessions.py index 376a2582..b01b1e09 100644 --- a/tests/utils/test_sessions.py +++ b/tests/utils/test_sessions.py @@ -1,3 +1,7 @@ +from uuid import UUID + +from mock import Mock, patch + from tests.helpers import create_ctfd, destroy_ctfd, login_as_user, register_user @@ -60,3 +64,35 @@ def test_session_invalidation_on_user_password_change(): # They should not be logged out assert r.status_code == 200 destroy_ctfd(app) + + +# @patch.object(uuid, 'uuid4', side_effect=TEST_UUIDS) +# @patch.object(uuid, 'uuid4') +def test_session_with_duplicate_session_id(): + app = create_ctfd() + with app.app_context(): + register_user(app) + register_user(app, name="user1", email="user1@examplectf.com") + + TEST_UUIDS = [ + # First user login successful + UUID("2d0ac3a8-b956-491a-9f53-d27cd33f2529"), + UUID("85e61378-5bc4-4cc8-a37e-b03270b7b172"), + # Second user gets a unique UUID then a duplicated one + UUID("c47c907f-d508-4f23-a28a-a1af1e9d3f27"), + UUID("85e61378-5bc4-4cc8-a37e-b03270b7b172"), + UUID("85e61378-5bc4-4cc8-a37e-b03270b7b172"), + UUID("85e61378-5bc4-4cc8-a37e-b03270b7b172"), + UUID("85e61378-5bc4-4cc8-a37e-b03270b7b172"), + UUID("85e61378-5bc4-4cc8-a37e-b03270b7b172"), + # Second user should finally receive a unique UUID + UUID("a00aff35-a12e-465a-8747-e18f78f60b13"), + UUID("da876038-7602-4bb0-88b8-f7104094219f"), + ] + uuid_mock = Mock(side_effect=TEST_UUIDS) + + with patch(target="CTFd.utils.sessions.uuid4", new=uuid_mock): + login_as_user(app) + with patch(target="CTFd.utils.sessions.uuid4", new=uuid_mock): + login_as_user(app, name="user1") + destroy_ctfd(app)