diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index 1947ec465..49c443e11 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -3397,6 +3397,8 @@ function set_user_loggedin_session($user) { session_regenerate_id(true); $_SESSION['mailcow_cc_username'] = $user; $_SESSION['mailcow_cc_role'] = 'user'; + // Update User-Agent after session regeneration to prevent validation errors + $_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT']; $sogo_sso_pass = file_get_contents("/etc/sogo-sso/sogo-sso.pass"); $_SESSION['sogo-sso-user-allowed'][] = $user; $_SESSION['sogo-sso-pass'] = $sogo_sso_pass; diff --git a/data/web/inc/sessions.inc.php b/data/web/inc/sessions.inc.php index 8f3192d70..7c44f1541 100644 --- a/data/web/inc/sessions.inc.php +++ b/data/web/inc/sessions.inc.php @@ -43,6 +43,9 @@ if (!isset($_SESSION['SESS_REMOTE_UA'])) { if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY'] > $SESSION_LIFETIME)) { session_unset(); session_destroy(); + session_start(); + // After destroying session, we need to reset the User-Agent for the new session + $_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT']; } $_SESSION['LAST_ACTIVITY'] = time(); @@ -134,6 +137,12 @@ function session_check() { return true; } if (!isset($_SESSION['SESS_REMOTE_UA']) || ($_SESSION['SESS_REMOTE_UA'] != $_SERVER['HTTP_USER_AGENT'])) { + // In development mode, allow User-Agent changes (e.g., for responsive testing in dev tools) + // Validate UA is not empty and has reasonable length (most UAs are under 200 chars, 500 is safe upper limit) + if (isset($GLOBALS['DEV_MODE']) && $GLOBALS['DEV_MODE'] && !empty($_SERVER['HTTP_USER_AGENT']) && strlen($_SERVER['HTTP_USER_AGENT']) < 500) { + $_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT']; + return true; + } $_SESSION['return'][] = array( 'type' => 'warning', 'msg' => 'session_ua' diff --git a/data/web/inc/triggers.admin.inc.php b/data/web/inc/triggers.admin.inc.php index df46a459c..4708b9e00 100644 --- a/data/web/inc/triggers.admin.inc.php +++ b/data/web/inc/triggers.admin.inc.php @@ -50,6 +50,8 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) { session_regenerate_id(true); $_SESSION['mailcow_cc_username'] = $login_user; $_SESSION['mailcow_cc_role'] = "admin"; + // Update User-Agent after session regeneration to prevent validation errors + $_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT']; header("Location: /admin/dashboard"); die(); } diff --git a/data/web/inc/triggers.domainadmin.inc.php b/data/web/inc/triggers.domainadmin.inc.php index a9f913688..187b27320 100644 --- a/data/web/inc/triggers.domainadmin.inc.php +++ b/data/web/inc/triggers.domainadmin.inc.php @@ -7,6 +7,8 @@ if (!empty($_GET['sso_token'])) { session_regenerate_id(true); $_SESSION['mailcow_cc_username'] = $username; $_SESSION['mailcow_cc_role'] = 'domainadmin'; + // Update User-Agent after session regeneration to prevent validation errors + $_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT']; header('Location: /domainadmin/mailbox'); } } @@ -61,6 +63,8 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) { session_regenerate_id(true); $_SESSION['mailcow_cc_username'] = $login_user; $_SESSION['mailcow_cc_role'] = "domainadmin"; + // Update User-Agent after session regeneration to prevent validation errors + $_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT']; header("Location: /domainadmin/mailbox"); die(); }