diff options
-rw-r--r-- | core/Nonce.php | 58 | ||||
-rw-r--r-- | plugins/Login/Controller.php | 51 | ||||
-rw-r--r-- | plugins/Login/lang/en.json | 7 | ||||
-rw-r--r-- | plugins/TwoFactorAuth/Controller.php | 8 |
4 files changed, 70 insertions, 54 deletions
diff --git a/core/Nonce.php b/core/Nonce.php index 74aea105d2..b4ae2a7a5c 100644 --- a/core/Nonce.php +++ b/core/Nonce.php @@ -65,38 +65,80 @@ class Nonce * * @param string $id The nonce's unique ID. See {@link getNonce()}. * @param string $cnonce Nonce sent from client. - * @param string $expectedReferrerHost The expected referrer host for the HTTP referrer URL. + * @param null|string $expectedReferrerHost The expected referrer host for the HTTP referrer URL. * @return bool `true` if valid; `false` otherwise. */ public static function verifyNonce($id, $cnonce, $expectedReferrerHost = null) { + // load error with message function. + $error = self::verifyNonceWithErrorMessage($id, $cnonce, $expectedReferrerHost); + return $error === ""; + } + + /** + * Returns error message + * + * A nonce is valid if it matches the current nonce and if the current nonce + * has not expired. + * + * The request is valid if the referrer is a local URL (see {@link Url::isLocalUrl()}) + * and if the HTTP origin is valid (see {@link getAcceptableOrigins()}). + * + * @param string $id The nonce's unique ID. See {@link getNonce()}. + * @param string $cnonce Nonce sent from client. + * @param null $expectedReferrerHost The expected referrer host for the HTTP referrer URL. + * @return string if empty is valid otherwise return error message + */ + public static function verifyNonceWithErrorMessage($id, $cnonce, $expectedReferrerHost = null) + { $ns = new SessionNamespace($id); $nonce = $ns->nonce; + $additionalErrors = ''; + + // The Session cookie is set to a secure cookie, when SSL is mis-configured, it can cause the PHP session cookie ID to change on each page view. + // Indicate to user how to solve this particular use case by forcing secure connections. + if (Url::isSecureConnectionAssumedByPiwikButNotForcedYet()) { + $additionalErrors = '<br/><br/>' . Piwik::translate('Login_InvalidNonceSSLMisconfigured', + array( + '<a target="_blank" rel="noreferrer noopener" href="https://matomo.org/faq/how-to/faq_91/">', + '</a>', + 'config/config.ini.php', + '<pre>force_ssl=1</pre>', + '<pre>[General]</pre>', + ) + ); + } + // validate token if (empty($cnonce) || $cnonce !== $nonce) { - return false; + return Piwik::translate('Login_InvalidNonceToken'); } // validate referrer $referrer = Url::getReferrer(); if (empty($expectedReferrerHost) && !empty($referrer) && !Url::isLocalUrl($referrer)) { - return false; + return Piwik::translate('Login_InvalidNonceReferrer', array( + '<a target="_blank" rel="noreferrer noopener" href="https://matomo.org/faq/how-to-install/#faq_98">', + '</a>' + )) . $additionalErrors; } + + //referrer is different expected host if (!empty($expectedReferrerHost) && !self::isReferrerHostValid($referrer, $expectedReferrerHost)) { - return false; + return Piwik::translate('Login_InvalidNonceUnexpectedReferrer') . $additionalErrors; } // validate origin $origin = self::getOrigin(); if (!empty($origin) && - ($origin == 'null' - || !in_array($origin, self::getAcceptableOrigins())) + ($origin == 'null' + || !in_array($origin, self::getAcceptableOrigins())) ) { - return false; + return Piwik::translate('Login_InvalidNonceOrigin') . $additionalErrors; } - return true; + return ''; } // public for tests diff --git a/plugins/Login/Controller.php b/plugins/Login/Controller.php index a1daed412e..b13f3f8231 100644 --- a/plugins/Login/Controller.php +++ b/plugins/Login/Controller.php @@ -133,7 +133,10 @@ class Controller extends \Piwik\Plugin\ControllerAdmin $form = new FormLogin(); if ($form->validate()) { $nonce = $form->getSubmitValue('form_nonce'); - if (Nonce::verifyNonce('Login.login', $nonce)) { + $messageNoAccess = Nonce::verifyNonceWithErrorMessage('Login.login', $nonce, null); + + // validate if there is error message + if ($messageNoAccess === "") { $loginOrEmail = $form->getSubmitValue('form_login'); $login = $this->getLoginFromLoginOrEmail($loginOrEmail); @@ -143,11 +146,9 @@ class Controller extends \Piwik\Plugin\ControllerAdmin } catch (Exception $e) { $messageNoAccess = $e->getMessage(); } - } else { - $messageNoAccess = $this->getMessageExceptionNoAccess(); } } - + if ($messageNoAccess) { http_response_code(403); } @@ -212,8 +213,9 @@ class Controller extends \Piwik\Plugin\ControllerAdmin if ($password) { $password = Common::unsanitizeInputValue($password); } - if (!Nonce::verifyNonce($nonceKey, $nonce)) { - $messageNoAccess = $this->getMessageExceptionNoAccess(); + $errorMessage = Nonce::verifyNonceWithErrorMessage($nonceKey, $nonce); + if ($errorMessage !== "") { + $messageNoAccess = $errorMessage; } elseif ($this->passwordVerify->isPasswordCorrect(Piwik::getCurrentUserLogin(), $password)) { $this->passwordVerify->setPasswordVerifiedCorrectly(); return; @@ -349,38 +351,6 @@ class Controller extends \Piwik\Plugin\ControllerAdmin Url::redirectToUrl($urlToRedirect); } - protected function getMessageExceptionNoAccess() - { - $message = Piwik::translate('Login_InvalidNonceOrHeadersOrReferrer', array('<a target="_blank" rel="noreferrer noopener" href="https://matomo.org/faq/how-to-install/#faq_98">', '</a>')); - - $message .= $this->getMessageExceptionNoAccessWhenInsecureConnectionMayBeUsed(); - - return $message; - } - - /** - * The Session cookie is set to a secure cookie, when SSL is mis-configured, it can cause the PHP session cookie ID to change on each page view. - * Indicate to user how to solve this particular use case by forcing secure connections. - * - * @return string - */ - protected function getMessageExceptionNoAccessWhenInsecureConnectionMayBeUsed() - { - $message = ''; - if(Url::isSecureConnectionAssumedByPiwikButNotForcedYet()) { - $message = '<br/><br/>' . Piwik::translate('Login_InvalidNonceSSLMisconfigured', - array( - '<a target="_blank" rel="noreferrer noopener" href="https://matomo.org/faq/how-to/faq_91/">', - '</a>', - 'config/config.ini.php', - '<pre>force_ssl=1</pre>', - '<pre>[General]</pre>', - ) - ); - } - return $message; - } - /** * Reset password action. Stores new password as hash and sends email * to confirm use. @@ -394,13 +364,14 @@ class Controller extends \Piwik\Plugin\ControllerAdmin $form = new FormResetPassword(); if ($form->validate()) { $nonce = $form->getSubmitValue('form_nonce'); - if (Nonce::verifyNonce('Login.login', $nonce)) { + $errorMessage = Nonce::verifyNonceWithErrorMessage('Login.login', $nonce); + if ($errorMessage === "") { $formErrors = $this->resetPasswordFirstStep($form); if (empty($formErrors)) { $infoMessage = Piwik::translate('Login_ConfirmationLinkSent'); } } else { - $formErrors = array($this->getMessageExceptionNoAccess()); + $formErrors = array($errorMessage); } } else { // if invalid, display error diff --git a/plugins/Login/lang/en.json b/plugins/Login/lang/en.json index 92597b4a0c..361c2c1b77 100644 --- a/plugins/Login/lang/en.json +++ b/plugins/Login/lang/en.json @@ -5,7 +5,10 @@ "ContactAdmin": "Possible reason: your host may have disabled the mail() function. <br \/>Please contact your Matomo administrator.", "ExceptionInvalidSuperUserAccessAuthenticationMethod": "A user with Super User access cannot be authenticated using the '%s' mechanism.", "ExceptionPasswordMD5HashExpected": "The password parameter is expected to be a MD5 hash of the password.", - "InvalidNonceOrHeadersOrReferrer": "Form security failed. Please reload the form and check that your cookies are enabled. If you use a proxy server, you must %1$s configure Matomo to accept the proxy header%2$s that forwards the Host header. Also, check that your Referrer header is sent correctly.", + "InvalidNonceToken": "Form security failed, token miss match. Please reload the form and check that your cookies are enabled.", + "InvalidNonceReferrer": "Form security failed, invalid referrer header. If you use a proxy server, you must %1$s configure Matomo to accept the proxy header %2$s that forwards the Host header. Also, check that your Referrer header is sent correctly and If you previously connected using https, please ensure you are connecting over a secure (SSL/TLS) connection and try again.", + "InvalidNonceUnexpectedReferrer" : "Form security failed, the referrer header is different from the expected referrer header, check that your Referrer header is sent correctly.", + "InvalidNonceOrigin": "Form security failed, invalid origin. If you previously connected using https, please ensure you are connecting over a secure (SSL/TLS) connection and try again.", "InvalidNonceSSLMisconfigured": "Also, you may %1$s force Matomo to use a secure connection%2$s: in your config file %3$s set %4$s below section %5$s", "InvalidOrExpiredToken": "Token is invalid or has expired.", "InvalidUsernameEmail": "Invalid username or e-mail address.", @@ -53,4 +56,4 @@ "SuspiciousLoginAttemptsInLastHourEmail5": "Additionally, if your Matomo has a limited set of users or IPs through which users will access it, it may be beneficial to setup a IP address allowlist. %1$sRead our docs for more information.%2$s", "LoginNotAllowedBecauseUserLoginBlocked": "Login functionality is temporarily disabled since we've a suspicious amount of failed login attempts in the last hour." } -}
\ No newline at end of file +} diff --git a/plugins/TwoFactorAuth/Controller.php b/plugins/TwoFactorAuth/Controller.php index daa97f21eb..e7936b5202 100644 --- a/plugins/TwoFactorAuth/Controller.php +++ b/plugins/TwoFactorAuth/Controller.php @@ -83,7 +83,8 @@ class Controller extends \Piwik\Plugin\Controller $form->removeAttribute('action'); // remove action attribute, otherwise hash part will be lost if ($form->validate()) { $nonce = $form->getSubmitValue('form_nonce'); - if ($nonce && Nonce::verifyNonce(self::LOGIN_2FA_NONCE, $nonce) && $form->validate()) { + $messageNoAccess = Nonce::verifyNonceWithErrorMessage(self::LOGIN_2FA_NONCE, $nonce); + if ($nonce && $messageNoAccess === "" && $form->validate()) { $authCode = $form->getSubmitValue('form_authcode'); if ($authCode && is_string($authCode)) { $authCode = str_replace('-', '', $authCode); @@ -106,8 +107,6 @@ class Controller extends \Piwik\Plugin\Controller // ignore error eg if login plugin is disabled } } - } else { - $messageNoAccess = Piwik::translate('Login_InvalidNonceOrHeadersOrReferrer', array('<a target="_blank" rel="noreferrer noopener" href="https://matomo.org/faq/how-to-install/#faq_98">', '</a>')); } } $superUsers = Request::processRequest('UsersManager.getUsersHavingSuperUserAccess', [], []); @@ -288,7 +287,8 @@ class Controller extends \Piwik\Plugin\Controller $this->validator->check2FaEnabled(); $regenerateNonce = Common::getRequestVar('regenerateNonce', '', 'string', $_POST); - $postedValidNonce = !empty($regenerateNonce) && Nonce::verifyNonce(self::REGENERATE_CODES_2FA_NONCE, $regenerateNonce); + $postedValidNonce = !empty($regenerateNonce) && Nonce::verifyNonce(self::REGENERATE_CODES_2FA_NONCE, + $regenerateNonce); $regenerateSuccess = false; $regenerateError = false; |