From c828a3cdb19903b85f21186c5017f3655f10ac60 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Wed, 29 Jul 2026 13:41:51 +0800 Subject: [PATCH 1/4] use HMAC-SHA256 for the cookie string to bypass bcrypt's 72-char limit --- modules/mfa/src/Auth/Process/Mfa.php | 5 +++-- modules/mfa/tests/MfaTest.php | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/mfa/src/Auth/Process/Mfa.php b/modules/mfa/src/Auth/Process/Mfa.php index b1b64fbe..297f7946 100644 --- a/modules/mfa/src/Auth/Process/Mfa.php +++ b/modules/mfa/src/Auth/Process/Mfa.php @@ -788,7 +788,8 @@ public static function isRememberMeCookieValid( // Check if value of expireDate is in future if ((int)$expireDate > time()) { $expectedString = self::generateRememberMeCookieString($rememberSecret, $state['employeeId'], $expireDate, $mfaOptions); - $isValid = password_verify($expectedString, $cookieHash); + $expectedHash = hash_hmac('sha256', $expectedString, $rememberSecret); + $isValid = hash_equals($expectedHash, $cookieHash); if ($isValid) { $idBrokerClient = self::getIdBrokerClient($state['idBrokerConfig']); @@ -888,7 +889,7 @@ public static function setRememberMeCookies( $secureCookie = Env::get('SECURE_COOKIE', true); $expireDate = strtotime($rememberDuration); $cookieString = self::generateRememberMeCookieString($rememberSecret, $employeeId, $expireDate, $mfaOptions); - $cookieHash = password_hash($cookieString, PASSWORD_DEFAULT); + $cookieHash = hash_hmac('sha256', $cookieString, $rememberSecret); setcookie('c1', base64_encode($cookieHash), $expireDate, '/', null, $secureCookie, true); setcookie('c2', $expireDate, $expireDate, '/', null, $secureCookie, true); } diff --git a/modules/mfa/tests/MfaTest.php b/modules/mfa/tests/MfaTest.php index b2b6b8d7..99e0b6a6 100644 --- a/modules/mfa/tests/MfaTest.php +++ b/modules/mfa/tests/MfaTest.php @@ -142,7 +142,7 @@ public function testIsRememberMeCookieValid_ValidCookie_UpdatesLastLogin(): void $expireDate, $mfaOptions ); - $cookieHash = password_hash($expectedString, PASSWORD_DEFAULT); + $cookieHash = hash_hmac('sha256', $expectedString, 'test-secret-for-unit-test'); $isValid = Mfa::isRememberMeCookieValid( $cookieHash, From f6e5d122db0817a9ac9415b903d6845fbee45c45 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Wed, 29 Jul 2026 13:47:43 +0800 Subject: [PATCH 2/4] the cookie secret doesn't need to be in the hashed string --- modules/mfa/src/Auth/Process/Mfa.php | 8 +++----- modules/mfa/tests/MfaTest.php | 1 - 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/modules/mfa/src/Auth/Process/Mfa.php b/modules/mfa/src/Auth/Process/Mfa.php index 297f7946..a027859d 100644 --- a/modules/mfa/src/Auth/Process/Mfa.php +++ b/modules/mfa/src/Auth/Process/Mfa.php @@ -787,7 +787,7 @@ public static function isRememberMeCookieValid( if (!empty($cookieHash) && !empty($expireDate) && is_numeric($expireDate)) { // Check if value of expireDate is in future if ((int)$expireDate > time()) { - $expectedString = self::generateRememberMeCookieString($rememberSecret, $state['employeeId'], $expireDate, $mfaOptions); + $expectedString = self::generateRememberMeCookieString($state['employeeId'], $expireDate, $mfaOptions); $expectedHash = hash_hmac('sha256', $expectedString, $rememberSecret); $isValid = hash_equals($expectedHash, $cookieHash); @@ -805,14 +805,12 @@ public static function isRememberMeCookieValid( /** * Generate and return a string to be hashed for remember me cookie - * @param string $rememberSecret * @param string $employeeId * @param int $expireDate * @param array $mfaOptions * @return string */ public static function generateRememberMeCookieString( - string $rememberSecret, string $employeeId, int $expireDate, array $mfaOptions @@ -824,7 +822,7 @@ public static function generateRememberMeCookieString( } } - return $rememberSecret . $employeeId . $expireDate . $allMfaIds; + return $employeeId . $expireDate . $allMfaIds; } /** @@ -888,7 +886,7 @@ public static function setRememberMeCookies( $rememberSecret = Env::requireEnv('REMEMBER_ME_SECRET'); $secureCookie = Env::get('SECURE_COOKIE', true); $expireDate = strtotime($rememberDuration); - $cookieString = self::generateRememberMeCookieString($rememberSecret, $employeeId, $expireDate, $mfaOptions); + $cookieString = self::generateRememberMeCookieString($employeeId, $expireDate, $mfaOptions); $cookieHash = hash_hmac('sha256', $cookieString, $rememberSecret); setcookie('c1', base64_encode($cookieHash), $expireDate, '/', null, $secureCookie, true); setcookie('c2', $expireDate, $expireDate, '/', null, $secureCookie, true); diff --git a/modules/mfa/tests/MfaTest.php b/modules/mfa/tests/MfaTest.php index 99e0b6a6..0c2d2830 100644 --- a/modules/mfa/tests/MfaTest.php +++ b/modules/mfa/tests/MfaTest.php @@ -137,7 +137,6 @@ public function testIsRememberMeCookieValid_ValidCookie_UpdatesLastLogin(): void putenv('REMEMBER_ME_SECRET=test-secret-for-unit-test'); try { $expectedString = Mfa::generateRememberMeCookieString( - 'test-secret-for-unit-test', $employeeId, $expireDate, $mfaOptions From 39e06b6dc28745587d674f496caf6893337d0d8f Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Wed, 29 Jul 2026 15:51:49 +0800 Subject: [PATCH 3/4] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- modules/mfa/tests/MfaTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mfa/tests/MfaTest.php b/modules/mfa/tests/MfaTest.php index 0c2d2830..6b08e0b0 100644 --- a/modules/mfa/tests/MfaTest.php +++ b/modules/mfa/tests/MfaTest.php @@ -141,7 +141,7 @@ public function testIsRememberMeCookieValid_ValidCookie_UpdatesLastLogin(): void $expireDate, $mfaOptions ); - $cookieHash = hash_hmac('sha256', $expectedString, 'test-secret-for-unit-test'); + $cookieHash = hash_hmac('sha256', $expectedString, (string)getenv('REMEMBER_ME_SECRET')); $isValid = Mfa::isRememberMeCookieValid( $cookieHash, From 8ef9e7941236ac910bc9bce432a1826ce8ba6192 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Wed, 29 Jul 2026 15:55:38 +0800 Subject: [PATCH 4/4] Format cookie value with separators --- modules/mfa/src/Auth/Process/Mfa.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mfa/src/Auth/Process/Mfa.php b/modules/mfa/src/Auth/Process/Mfa.php index a027859d..9f41950c 100644 --- a/modules/mfa/src/Auth/Process/Mfa.php +++ b/modules/mfa/src/Auth/Process/Mfa.php @@ -822,7 +822,7 @@ public static function generateRememberMeCookieString( } } - return $employeeId . $expireDate . $allMfaIds; + return $employeeId . '|' . $expireDate . '|' . $allMfaIds; } /**