From d075cdeaf39479c9e2de59708089717f92364661 Mon Sep 17 00:00:00 2001 From: Oliver Hader Date: Sun, 15 Dec 2019 20:34:47 +0100 Subject: [PATCH] [TASK] Streamline frontend user password recovery process The ext:felogin recovery process is using a non-typesafe comparison which might be exploited with a probability of 0.000000294% and is storing the recovery token as plain MD5-hash in database. In order to streamline the process non-typesafe comparison is using PHP's hash_equals() method; for keeping backward compatibility just HMAC-SHA1 is applied to the recovery token in database. Since exploitations to this scenario are very unlikely (for a 50% chance an attacker would have to trigger the creation of around 170 million recovery requests) it is not handled with a security workflow - but using the public workflow. Resolves: #89952 Releases: master, 10.2, 9.5, 8.7 Change-Id: Idcb7b7d6eb418124dc17f1707284b6abe8a8b63b Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/62693 Tested-by: Oliver Hader Reviewed-by: Oliver Hader --- .../Classes/Controller/FrontendLoginController.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/typo3/sysext/felogin/Classes/Controller/FrontendLoginController.php b/typo3/sysext/felogin/Classes/Controller/FrontendLoginController.php index 6ea5b751be17..ecff7bcf209a 100644 --- a/typo3/sysext/felogin/Classes/Controller/FrontendLoginController.php +++ b/typo3/sysext/felogin/Classes/Controller/FrontendLoginController.php @@ -340,7 +340,13 @@ protected function changePassword() $user = $this->pi_getRecord('fe_users', (int)$uid); $userHash = $user['felogin_forgotHash']; $compareHash = explode('|', $userHash); - if (!$compareHash || !$compareHash[1] || $compareHash[0] < time() || $hash[0] != $compareHash[0] || md5($hash[1]) != $compareHash[1]) { + if (strlen($compareHash[1]) === 40) { + $hashEquals = hash_equals($compareHash[1], GeneralUtility::hmac((string)$hash[1])); + } else { + // backward-compatibility for previous MD5 hashes + $hashEquals = hash_equals($compareHash[1], md5($hash[1])); + } + if (!$compareHash || !$compareHash[1] || $compareHash[0] < time() || !hash_equals($compareHash[0], $hash[0]) || !$hashEquals) { $markerArray['###STATUS_MESSAGE###'] = $this->getDisplayText( 'change_password_notvalid_message', $this->conf['changePasswordNotValidMessage_stdWrap.'] @@ -459,7 +465,7 @@ protected function generateAndSendHash($user) $validEndString = date($this->conf['dateFormat'], $validEnd); $hash = md5(GeneralUtility::makeInstance(Random::class)->generateRandomBytes(64)); $randHash = $validEnd . '|' . $hash; - $randHashDB = $validEnd . '|' . md5($hash); + $randHashDB = $validEnd . '|' . GeneralUtility::hmac($hash); // Write hash to DB $userTable = $this->frontendController->fe_user->user_table;