diff --git a/src/applications/auth/controller/PhabricatorAuthRevokeTokenController.php b/src/applications/auth/controller/PhabricatorAuthRevokeTokenController.php --- a/src/applications/auth/controller/PhabricatorAuthRevokeTokenController.php +++ b/src/applications/auth/controller/PhabricatorAuthRevokeTokenController.php @@ -46,7 +46,7 @@ if ($request->isDialogFormPost()) { foreach ($tokens as $token) { - $token->setTokenExpires(PhabricatorTime::getNow() - 1)->save(); + $token->revokeToken(); } return id(new AphrontRedirectResponse())->setURI($panel_uri); } diff --git a/src/applications/auth/storage/PhabricatorAuthTemporaryToken.php b/src/applications/auth/storage/PhabricatorAuthTemporaryToken.php --- a/src/applications/auth/storage/PhabricatorAuthTemporaryToken.php +++ b/src/applications/auth/storage/PhabricatorAuthTemporaryToken.php @@ -44,6 +44,30 @@ return false; } + public function revokeToken() { + if ($this->isRevocable()) { + $this->setTokenExpires(PhabricatorTime::getNow() - 1)->save(); + } + return $this; + } + + public static function revokeTokens( + PhabricatorUser $viewer, + array $object_phids, + array $token_types) { + + $tokens = id(new PhabricatorAuthTemporaryTokenQuery()) + ->setViewer($viewer) + ->withObjectPHIDs($object_phids) + ->withTokenTypes($token_types) + ->withExpired(false) + ->execute(); + + foreach ($tokens as $token) { + $token->revokeToken(); + } + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/people/editor/PhabricatorUserEditor.php b/src/applications/people/editor/PhabricatorUserEditor.php --- a/src/applications/people/editor/PhabricatorUserEditor.php +++ b/src/applications/people/editor/PhabricatorUserEditor.php @@ -427,6 +427,8 @@ $user->endWriteLocking(); $user->saveTransaction(); + $this->revokePasswordResetLinks($user); + return $this; } @@ -490,6 +492,9 @@ } $email->sendNewPrimaryEmail($user); + + $this->revokePasswordResetLinks($user); + return $this; } @@ -575,4 +580,19 @@ } } + private function revokePasswordResetLinks(PhabricatorUser $user) { + // Revoke any outstanding password reset links. If an attacker compromises + // an account, changes the email address, and sends themselves a password + // reset link, it could otherwise remain live for a short period of time + // and allow them to compromise the account again later. + + PhabricatorAuthTemporaryToken::revokeTokens( + $user, + array($user->getPHID()), + array( + PhabricatorAuthSessionEngine::ONETIME_TEMPORARY_TOKEN_TYPE, + PhabricatorAuthSessionEngine::PASSWORD_TEMPORARY_TOKEN_TYPE, + )); + } + } diff --git a/src/applications/settings/panel/PhabricatorSettingsPanelEmailAddresses.php b/src/applications/settings/panel/PhabricatorSettingsPanelEmailAddresses.php --- a/src/applications/settings/panel/PhabricatorSettingsPanelEmailAddresses.php +++ b/src/applications/settings/panel/PhabricatorSettingsPanelEmailAddresses.php @@ -276,9 +276,14 @@ ->setUser($user) ->addHiddenInput('delete', $email_id) ->setTitle(pht("Really delete address '%s'?", $address)) - ->appendChild(phutil_tag('p', array(), pht( - 'Are you sure you want to delete this address? You will no '. - 'longer be able to use it to login.'))) + ->appendParagraph( + pht( + 'Are you sure you want to delete this address? You will no '. + 'longer be able to use it to login.')) + ->appendParagraph( + pht( + 'Note: Removing an email address from your account will invalidate '. + 'any outstanding password reset links.')) ->addSubmitButton(pht('Delete')) ->addCancelButton($uri); @@ -359,10 +364,15 @@ ->setUser($user) ->addHiddenInput('primary', $email_id) ->setTitle(pht('Change primary email address?')) - ->appendChild(phutil_tag('p', array(), pht( - 'If you change your primary address, Phabricator will send'. - ' all email to %s.', - $address))) + ->appendParagraph( + pht( + 'If you change your primary address, Phabricator will send all '. + 'email to %s.', + $address)) + ->appendParagraph( + pht( + 'Note: Changing your primary email address will invalidate any '. + 'outstanding password reset links.')) ->addSubmitButton(pht('Change Primary Address')) ->addCancelButton($uri);