From 5ae582b1a150c4245efa5e2e795aa0fad175b624 Mon Sep 17 00:00:00 2001 From: SteveGilvarry Date: Fri, 12 Jun 2026 20:25:49 +1000 Subject: [PATCH 1/2] fix: emit a single session cookie on login refs #2471 On successful login auth.php called zm_session_clear() followed by zm_session_regenerate_id(), which together emitted three Set-Cookie ZMSESSID headers: a deletion, a throwaway intermediate id, and the final authenticated id. This is the multiple-cookie behaviour reported in #2471. Add zm_session_regenerate_id_login(), which clears the pre-auth session data and calls session_regenerate_id(true) to issue one new id while deleting the old session server-side. Same anti-session-fixation guarantee in a single Set-Cookie. Logout (zm_session_clear) and the periodic mid-session regeneration are left unchanged. Co-Authored-By: Claude Opus 4.8 --- web/includes/auth.php | 4 ++-- web/includes/session.php | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/web/includes/auth.php b/web/includes/auth.php index a1d45c74b..768ec578e 100644 --- a/web/includes/auth.php +++ b/web/includes/auth.php @@ -635,8 +635,8 @@ if (ZM_OPT_USE_AUTH) { } // end if success==false } // end if using reCaptcha - zm_session_clear(); # Closes session - zm_session_regenerate_id(); # starts session + # Drop the pre-auth session and issue a fresh id in a single Set-Cookie + zm_session_regenerate_id_login(); $username = $_REQUEST['username']; $password = $_REQUEST['password']; diff --git a/web/includes/session.php b/web/includes/session.php index 3139336af..c8c78e9f5 100644 --- a/web/includes/session.php +++ b/web/includes/session.php @@ -92,6 +92,25 @@ function zm_session_regenerate_id() { : $_SERVER['REMOTE_ADDR']; } // function zm_session_regenerate_id() +// Regenerate the session id at a privilege boundary (login) in a single +// Set-Cookie. Unlike zm_session_clear()+zm_session_regenerate_id(), which emit +// three Set-Cookie headers (a delete plus two fresh starts), this drops any +// pre-auth session data and uses session_regenerate_id(true) to issue one new id +// while deleting the old session server-side. Same anti-fixation guarantee, one +// cookie. Assumes zm_session_start() has been called previously. +function zm_session_regenerate_id_login() { + if (!is_session_started()) zm_session_start(); + // Discard any pre-auth session contents so nothing carries across the + // authentication boundary. + $_SESSION = array(); + // New id + delete the old session file server-side. Emits a single Set-Cookie. + session_regenerate_id(true); + $_SESSION['generated_at'] = time(); + $_SESSION['remoteAddr'] = !empty($_SERVER['HTTP_X_FORWARDED_FOR']) + ? trim(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0]) + : $_SERVER['REMOTE_ADDR']; +} // function zm_session_regenerate_id_login() + function is_session_started() { if ( php_sapi_name() !== 'cli' ) { if ( version_compare(phpversion(), '5.4.0', '>=') ) { From a2d6319b0bb4f0f32e9b15f5a14b82c13d2bd8e2 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 12 Jun 2026 19:46:57 -0400 Subject: [PATCH 2/2] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- web/includes/session.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/web/includes/session.php b/web/includes/session.php index c8c78e9f5..37e0dd761 100644 --- a/web/includes/session.php +++ b/web/includes/session.php @@ -92,12 +92,11 @@ function zm_session_regenerate_id() { : $_SERVER['REMOTE_ADDR']; } // function zm_session_regenerate_id() -// Regenerate the session id at a privilege boundary (login) in a single -// Set-Cookie. Unlike zm_session_clear()+zm_session_regenerate_id(), which emit -// three Set-Cookie headers (a delete plus two fresh starts), this drops any -// pre-auth session data and uses session_regenerate_id(true) to issue one new id -// while deleting the old session server-side. Same anti-fixation guarantee, one -// cookie. Assumes zm_session_start() has been called previously. +// Regenerate the session id at a privilege boundary (login). +// When called with an already-started session (the normal login flow), this +// should emit a single Set-Cookie via session_regenerate_id(true) while +// discarding any pre-auth session data and deleting the old session server-side. +// Assumes zm_session_start() has been called previously. function zm_session_regenerate_id_login() { if (!is_session_started()) zm_session_start(); // Discard any pre-auth session contents so nothing carries across the