Commit 05e95eb4 authored by catch's avatar catch
Browse files

Issue #3327294 by poker10, alexpott: Username enumeration via one time login...

Issue #3327294 by poker10, alexpott: Username enumeration via one time login route when logged in as another user
parent e36422dd
Loading
Loading
Loading
Loading
+25 −4
Original line number Diff line number Diff line
@@ -133,7 +133,8 @@ public function resetPass(Request $request, $uid, $timestamp, $hash) {
      // A different user is already logged in on the computer.
      else {
        /** @var \Drupal\user\UserInterface $reset_link_user */
        if ($reset_link_user = $this->userStorage->load($uid)) {
        $reset_link_user = $this->userStorage->load($uid);
        if ($reset_link_user && $this->validatePathParameters($reset_link_user, $timestamp, $hash)) {
          $this->messenger()
            ->addWarning($this->t('Another user (%other_user) is already logged into the site on this computer, but you tried to use a one-time link for user %resetting_user. Please <a href=":logout">log out</a> and try using the link again.',
              [
@@ -302,7 +303,7 @@ protected function determineErrorRedirect(?UserInterface $user, int $timestamp,
      $this->messenger()->addError($this->t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'));
      return $this->redirect('user.pass');
    }
    elseif ($user->isAuthenticated() && ($timestamp >= $user->getLastLoginTime()) && ($timestamp <= $current) && hash_equals($hash, user_pass_rehash($user, $timestamp))) {
    elseif ($user->isAuthenticated() && $this->validatePathParameters($user, $timestamp, $hash, $timeout)) {
      // The information provided is valid.
      return NULL;
    }
@@ -311,6 +312,27 @@ protected function determineErrorRedirect(?UserInterface $user, int $timestamp,
    return $this->redirect('user.pass');
  }

  /**
   * Validates hash and timestamp.
   *
   * @param \Drupal\user\UserInterface $user
   *   User requesting reset.
   * @param int $timestamp
   *   The timestamp.
   * @param string $hash
   *   Login link hash.
   * @param int $timeout
   *   Link expiration timeout.
   *
   * @return bool
   *   Whether the provided data are valid.
   */
  protected function validatePathParameters(UserInterface $user, int $timestamp, string $hash, int $timeout = 0): bool {
    $current = \Drupal::time()->getRequestTime();
    $timeout_valid = ((!empty($timeout) && $current - $timestamp < $timeout) || empty($timeout));
    return ($timestamp >= $user->getLastLoginTime()) && $timestamp <= $current && $timeout_valid && hash_equals($hash, user_pass_rehash($user, $timestamp));
  }

  /**
   * Redirects users to their profile page.
   *
@@ -382,13 +404,12 @@ public function logout() {
  public function confirmCancel(UserInterface $user, $timestamp = 0, $hashed_pass = '') {
    // Time out in seconds until cancel URL expires; 24 hours = 86400 seconds.
    $timeout = 86400;
    $current = REQUEST_TIME;

    // Basic validation of arguments.
    $account_data = $this->userData->get('user', $user->id());
    if (isset($account_data['cancel_method']) && !empty($timestamp) && !empty($hashed_pass)) {
      // Validate expiration and hashed password/login.
      if ($timestamp <= $current && $current - $timestamp < $timeout && $user->id() && $timestamp >= $user->getLastLoginTime() && hash_equals($hashed_pass, user_pass_rehash($user, $timestamp))) {
      if ($user->id() && $this->validatePathParameters($user, $timestamp, $hashed_pass, $timeout)) {
        $edit = [
          'user_cancel_notify' => $account_data['cancel_notify'] ?? $this->config('user.settings')->get('notify.status_canceled'),
        ];
+7 −0
Original line number Diff line number Diff line
@@ -339,6 +339,13 @@ public function testUserPasswordResetLoggedIn() {
    $this->assertSession()->linkExists('log out');
    $this->assertSession()->linkByHrefExists(Url::fromRoute('user.logout')->toString());

    // Verify that the invalid password reset page does not show the user name.
    $attack_reset_url = "user/reset/" . $another_account->id() . "/1/1";
    $this->drupalGet($attack_reset_url);
    $this->assertSession()->pageTextNotContains($another_account->getAccountName());
    $this->assertSession()->addressEquals('user/' . $this->account->id());
    $this->assertSession()->pageTextContains('The one-time login link you clicked is invalid.');

    $another_account->delete();
    $this->drupalGet($resetURL);
    $this->assertSession()->pageTextContains('The one-time login link you clicked is invalid.');
+1 −1
Original line number Diff line number Diff line
@@ -2117,7 +2117,7 @@ parameters:

		-
			message: "#^Call to deprecated constant REQUEST_TIME\\: Deprecated in drupal\\:8\\.3\\.0 and is removed from drupal\\:11\\.0\\.0\\. Use \\\\Drupal\\:\\:time\\(\\)\\-\\>getRequestTime\\(\\); $#"
			count: 2
			count: 1
			path: modules/user/src/Controller/UserController.php

		-