aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorStephan Orbaugh <62374139+sorbaugh@users.noreply.github.com>2024-12-20 14:53:11 +0100
committerGitHub <noreply@github.com>2024-12-20 14:53:11 +0100
commitd4715c61f283b8365652930357315a4854310513 (patch)
tree1454f1532ea6e37f0c98e43647cba521d580d46c /core
parent2d76d136a9aa6441e0379c84f9b852f69657466d (diff)
parentd977265b6607dadcd5cb9d2ec532c2f76c77c042 (diff)
downloadnextcloud-server-d4715c61f283b8365652930357315a4854310513.tar.gz
nextcloud-server-d4715c61f283b8365652930357315a4854310513.zip
Merge pull request #49560 from nextcloud/fix/login-origin
feat(login): add origin check at login
Diffstat (limited to 'core')
-rw-r--r--core/Controller/LoginController.php35
1 files changed, 27 insertions, 8 deletions
diff --git a/core/Controller/LoginController.php b/core/Controller/LoginController.php
index d4d6a17db79..19d5aae9613 100644
--- a/core/Controller/LoginController.php
+++ b/core/Controller/LoginController.php
@@ -41,12 +41,14 @@ use OCP\IUser;
use OCP\IUserManager;
use OCP\Notification\IManager;
use OCP\Security\Bruteforce\IThrottler;
+use OCP\Security\ITrustedDomainHelper;
use OCP\Util;
class LoginController extends Controller {
public const LOGIN_MSG_INVALIDPASSWORD = 'invalidpassword';
public const LOGIN_MSG_USERDISABLED = 'userdisabled';
public const LOGIN_MSG_CSRFCHECKFAILED = 'csrfCheckFailed';
+ public const LOGIN_MSG_INVALID_ORIGIN = 'invalidOrigin';
public function __construct(
?string $appName,
@@ -167,6 +169,9 @@ class LoginController extends Controller {
Util::addHeader('meta', ['property' => 'og:type', 'content' => 'website']);
Util::addHeader('meta', ['property' => 'og:image', 'content' => $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-touch.png'))]);
+ // Add same-origin referrer policy so we can check for valid requests
+ Util::addHeader('meta', ['name' => 'referrer', 'content' => 'same-origin']);
+
$parameters = [
'alt_login' => OC_App::getAlternativeLogIns(),
'pageTitle' => $this->l10n->t('Login'),
@@ -269,29 +274,42 @@ class LoginController extends Controller {
return new RedirectResponse($this->urlGenerator->linkToDefaultPageUrl());
}
- /**
- * @return RedirectResponse
- */
#[NoCSRFRequired]
#[PublicPage]
#[BruteForceProtection(action: 'login')]
#[UseSession]
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
#[FrontpageRoute(verb: 'POST', url: '/login')]
- public function tryLogin(Chain $loginChain,
+ public function tryLogin(
+ Chain $loginChain,
+ ITrustedDomainHelper $trustedDomainHelper,
string $user = '',
string $password = '',
?string $redirect_url = null,
string $timezone = '',
- string $timezone_offset = ''): RedirectResponse {
- if (!$this->request->passesCSRFCheck()) {
+ string $timezone_offset = '',
+ ): RedirectResponse {
+ $error = '';
+
+ $origin = $this->request->getHeader('Origin');
+ $throttle = true;
+ if ($origin === '' || !$trustedDomainHelper->isTrustedUrl($origin)) {
+ // Login attempt not from the same origin,
+ // We only allow this on the login flow but not on the UI login page.
+ // This could have come from someone malicious who tries to block a user by triggering the bruteforce protection.
+ $error = self::LOGIN_MSG_INVALID_ORIGIN;
+ $throttle = false;
+ } elseif (!$this->request->passesCSRFCheck()) {
if ($this->userSession->isLoggedIn()) {
// If the user is already logged in and the CSRF check does not pass then
// simply redirect the user to the correct page as required. This is the
// case when a user has already logged-in, in another tab.
return $this->generateRedirect($redirect_url);
}
+ $error = self::LOGIN_MSG_CSRFCHECKFAILED;
+ }
+ if ($error !== '') {
// Clear any auth remnants like cookies to ensure a clean login
// For the next attempt
$this->userSession->logout();
@@ -299,8 +317,8 @@ class LoginController extends Controller {
$user,
$user,
$redirect_url,
- self::LOGIN_MSG_CSRFCHECKFAILED,
- false,
+ $error,
+ $throttle,
);
}
@@ -371,6 +389,7 @@ class LoginController extends Controller {
$this->session->set('loginMessages', [
[$loginMessage], []
]);
+
return $response;
}