Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion com.woltlab.wcf/templates/email_lostPassword.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
{lang}wcf.user.lostPassword.mail.html.intro{/lang}

{capture assign=button}
<a href="{link controller='NewPassword' object=$mailbox->getUser() isHtmlEmail=true}k={$mailbox->getUser()->lostPasswordKey}{/link}">
<a href="{link controller='NewPassword' object=$mailbox->getUser() isHtmlEmail=true}k={$lostPasswordKey}{/link}">
{lang}wcf.user.lostPassword.mail.html.reset{/lang}
</a>
{/capture}
Expand Down
2 changes: 1 addition & 1 deletion com.woltlab.wcf/templates/email_sendNewPassword.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
{lang}wcf.acp.user.sendNewPassword.mail.html.intro{/lang}

{capture assign=button}
<a href="{link controller='NewPassword' object=$mailbox->getUser() isHtmlEmail=true}k={$mailbox->getUser()->lostPasswordKey}{/link}">
<a href="{link controller='NewPassword' object=$mailbox->getUser() isHtmlEmail=true}k={$lostPasswordKey}{/link}">
{lang}wcf.acp.user.sendNewPassword.mail.html.reset{/lang}
</a>
{/capture}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
*/

use wcf\system\database\table\column\CharDatabaseTableColumn;
use wcf\system\database\table\column\DefaultFalseBooleanDatabaseTableColumn;
use wcf\system\database\table\column\JsonDatabaseTableColumn;
use wcf\system\database\table\column\MediumintDatabaseTableColumn;
Expand Down Expand Up @@ -62,4 +63,9 @@
NotNullVarchar255DatabaseTableColumn::create('emoji')
->defaultValue(''),
]),
PartialDatabaseTable::create('wcf1_user')
->columns([
CharDatabaseTableColumn::create('lostPasswordKey')
->length(64),
]),
];
2 changes: 1 addition & 1 deletion wcfsetup/install/files/lib/data/user/User.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
* @property-read int $activationCode flag which determines, whether the user is activated (for legacy reasons an random integer, if the user is *not* activated)
* @property-read ?string $emailConfirmed code sent to the user's email address used for account activation or null if the email is confirmed
* @property-read int $lastLostPasswordRequestTime timestamp at which the user has reported that they lost their password or 0 if password has not been reported as lost
* @property-read ?string $lostPasswordKey code used for authenticating setting new password after password loss or empty if password has not been reported as lost
* @property-read ?string $lostPasswordKey SHA-256 hash of the code used for authenticating setting new password after password loss or empty if password has not been reported as lost
* @property-read int $lastUsernameChange timestamp at which the user changed their name the last time or 0 if username has not been changed
* @property-read string $newEmail new email address of the user that has to be manually confirmed or empty if no new email address has been set
* @property-read string $oldUsername previous name of the user or empty if they have had no previous name
Expand Down
8 changes: 4 additions & 4 deletions wcfsetup/install/files/lib/form/LostPasswordForm.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,10 @@ public function save()
// generate a new lost password key
$lostPasswordKey = Hex::encode(\random_bytes(20));

// save key and request time in database
// save hashed key and request time in database
$this->objectAction = new UserAction([$this->user], 'update', [
'data' => \array_merge($this->additionalFields, [
'lostPasswordKey' => $lostPasswordKey,
'lostPasswordKey' => \hash('sha256', $lostPasswordKey),
'lastLostPasswordRequestTime' => \TIME_NOW,
]),
]);
Expand All @@ -174,8 +174,8 @@ public function save()
$email->addRecipient(new UserMailbox($this->user));
$email->setSubject($this->user->getLanguage()->getDynamicVariable('wcf.user.lostPassword.mail.subject'));
$email->setBody(new MimePartFacade([
new RecipientAwareTextMimePart('text/html', 'email_lostPassword'),
new RecipientAwareTextMimePart('text/plain', 'email_lostPassword'),
new RecipientAwareTextMimePart('text/html', 'email_lostPassword', 'wcf', ['lostPasswordKey' => $lostPasswordKey]),
new RecipientAwareTextMimePart('text/plain', 'email_lostPassword', 'wcf', ['lostPasswordKey' => $lostPasswordKey]),
]));
$email->send();

Expand Down
6 changes: 3 additions & 3 deletions wcfsetup/install/files/lib/form/NewPasswordForm.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public function readParameters()
if (!$this->user->lostPasswordKey) {
$this->throwInvalidLinkException();
}
if (!\hash_equals($this->user->lostPasswordKey, $this->lostPasswordKey)) {
if (!\hash_equals($this->user->lostPasswordKey, \hash('sha256', $this->lostPasswordKey))) {
$this->throwInvalidLinkException();
}
// expire lost password requests after a day
Expand All @@ -66,7 +66,7 @@ public function readParameters()

WCF::getSession()->register('lostPasswordRequest', [
'userID' => $this->user->userID,
'key' => $this->user->lostPasswordKey,
'key' => $this->lostPasswordKey,
]);
} else {
if (!\is_array(WCF::getSession()->getVar('lostPasswordRequest'))) {
Expand All @@ -78,7 +78,7 @@ public function readParameters()
if (!$this->user->userID) {
throw new IllegalLinkException();
}
if (!\hash_equals($this->user->lostPasswordKey, WCF::getSession()->getVar('lostPasswordRequest')['key'])) {
if (!\hash_equals($this->user->lostPasswordKey, \hash('sha256', WCF::getSession()->getVar('lostPasswordRequest')['key']))) {
$this->throwInvalidLinkException();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ class SendNewPasswordWorker extends AbstractWorker
*/
protected $limit = 20;

/**
* @var array<int, string>
*/
private array $lostPasswordKeys = [];

#[\Override]
public function countObjects()
{
Expand Down Expand Up @@ -104,11 +109,13 @@ protected function resetPassword(UserEditor $userEditor)
$userAction = new UserAction([$userEditor], 'update', [
'data' => [
'password' => null,
'lostPasswordKey' => $lostPasswordKey,
'lostPasswordKey' => \hash('sha256', $lostPasswordKey),
'lastLostPasswordRequestTime' => $lastLostPasswordRequestTime,
],
]);
$userAction->executeAction();

$this->lostPasswordKeys[$userEditor->userID] = $lostPasswordKey;
}

/**
Expand All @@ -128,9 +135,10 @@ protected function sendLink(User $user)
));
$email->addRecipient(new UserMailbox($user));
$email->setSubject($user->getLanguage()->getDynamicVariable('wcf.acp.user.sendNewPassword.mail.subject'));
$lostPasswordKey = $this->lostPasswordKeys[$user->userID] ?? '';
$email->setBody(new MimePartFacade([
new RecipientAwareTextMimePart('text/html', 'email_sendNewPassword'),
new RecipientAwareTextMimePart('text/plain', 'email_sendNewPassword'),
new RecipientAwareTextMimePart('text/html', 'email_sendNewPassword', 'wcf', ['lostPasswordKey' => $lostPasswordKey]),
new RecipientAwareTextMimePart('text/plain', 'email_sendNewPassword', 'wcf', ['lostPasswordKey' => $lostPasswordKey]),
]));
$jobs = $email->getJobs();
foreach ($jobs as $job) {
Expand Down
4 changes: 2 additions & 2 deletions wcfsetup/install/lang/de.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3215,7 +3215,7 @@ erforderlich, dass {if LANGUAGE_USE_INFORMAL_VARIANT}du{else}Sie{/if} ein neues
Benutzerkonto {@$mailbox->getUser()->username} auf der Seite {@PAGE_TITLE|phrase} [URL:{link isEmail=true}{/link}]
weiterhin verwenden {if LANGUAGE_USE_INFORMAL_VARIANT}kannst{else}können{/if}:

{link controller='NewPassword' object=$mailbox->getUser() isEmail=true}k={@$mailbox->getUser()->lostPasswordKey}{/link} {* this line ends with a space *}
{link controller='NewPassword' object=$mailbox->getUser() isEmail=true}k={@$lostPasswordKey}{/link} {* this line ends with a space *}

{if LANGUAGE_USE_INFORMAL_VARIANT}Solltest du{else}Sollten Sie{/if} diese Nachricht erst nach dem {@$mailbox->getUser()->lastLostPasswordRequestTime+86400|plainTime} lesen, ist es
aus Sicherheitsgründen erforderlich, dass {if LANGUAGE_USE_INFORMAL_VARIANT}du{else}Sie{/if} die Kennwort vergessen-Funktion [URL:{link controller='LostPassword' isEmail=true}{/link}] {if LANGUAGE_USE_INFORMAL_VARIANT}nutzt{else}nutzen{/if}.]]></item>
Expand Down Expand Up @@ -4607,7 +4607,7 @@ Erlaubte Dateiendungen: gif, jpg, jpeg, png, webp]]></item>
{@$mailbox->getUser()->username} auf der Seite {@PAGE_TITLE|phrase} [URL:{link isEmail=true}{/link}] vergessen zu haben. {if LANGUAGE_USE_INFORMAL_VARIANT}Du kannst dein{else}Sie können Ihr{/if} Kennwort
nach einem Klick auf den folgenden Link ändern:

{link controller='NewPassword' object=$mailbox->getUser() isEmail=true}k={@$mailbox->getUser()->lostPasswordKey}{/link} {* this line ends with a space *}
{link controller='NewPassword' object=$mailbox->getUser() isEmail=true}k={@$lostPasswordKey}{/link} {* this line ends with a space *}

Wenn {if LANGUAGE_USE_INFORMAL_VARIANT}du dein{else}Sie Ihr{/if} Kennwort nicht ändern {if LANGUAGE_USE_INFORMAL_VARIANT}möchtest{else}möchten{/if}, dann wird diese Anfrage
am {@$mailbox->getUser()->lastLostPasswordRequestTime+86400|plainTime} automatisch ablaufen.]]></item>
Expand Down
4 changes: 2 additions & 2 deletions wcfsetup/install/lang/en.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3142,7 +3142,7 @@ If you have <strong>already bought the licenses for the listed apps</strong>, th
An administrator has reset your password. You are now required to set a new password to be able to use your
user account {@$mailbox->getUser()->username} on the website {@PAGE_TITLE|phrase} [URL:{link isEmail=true}{/link}] again:

{link controller='NewPassword' object=$mailbox->getUser() isEmail=true}k={@$mailbox->getUser()->lostPasswordKey}{/link} {* this line ends with a space *}
{link controller='NewPassword' object=$mailbox->getUser() isEmail=true}k={@$lostPasswordKey}{/link} {* this line ends with a space *}

If you read this message after {@$mailbox->getUser()->lastLostPasswordRequestTime+86400|plainTime} you’ll have to use
the lost password form [URL:{link controller='LostPassword' isEmail=true}{/link}] for security reasons.]]></item>
Expand Down Expand Up @@ -4606,7 +4606,7 @@ You (or someone else) claimed to have lost the password for the user account {@$
the website {@PAGE_TITLE|phrase} [URL:{link isEmail=true}{/link}]. You can change your password after clicking
the following link:

{link controller='NewPassword' object=$mailbox->getUser() isEmail=true}k={@$mailbox->getUser()->lostPasswordKey}{/link} {* this line ends with a space *}
{link controller='NewPassword' object=$mailbox->getUser() isEmail=true}k={@$lostPasswordKey}{/link} {* this line ends with a space *}

If you don’t want to change your password you can simply wait. The request will expire at {@$mailbox->getUser()->lastLostPasswordRequestTime+86400|plainTime}.]]></item>
<item name="wcf.user.lostPassword.mail.html.headline"><![CDATA[Dear {$mailbox->getUser()->username},]]></item>
Expand Down
2 changes: 1 addition & 1 deletion wcfsetup/setup/db/install_com.woltlab.wcf.php
Original file line number Diff line number Diff line change
Expand Up @@ -3786,7 +3786,7 @@
NotNullInt10DatabaseTableColumn::create('lastLostPasswordRequestTime')
->defaultValue(0),
CharDatabaseTableColumn::create('lostPasswordKey')
->length(40),
->length(64),
NotNullInt10DatabaseTableColumn::create('lastUsernameChange')
->defaultValue(0),
NotNullVarchar255DatabaseTableColumn::create('newEmail')
Expand Down
Loading