From eda4c5a030dfbfd2b37f83db740b523c67dfa3d7 Mon Sep 17 00:00:00 2001 From: Anonymous Contributor Date: Wed, 23 Aug 2023 18:16:36 +0200 Subject: [PATCH] Finalize "forgot password" feature --- app/utils.php | 11 +++++---- pages/forgot.php | 14 +++++++---- pages/reset-password.php | 45 ++++++++++++++++++++++++----------- templates/forgot.html | 4 ++-- templates/mail.html | 35 +++++++++++++-------------- templates/reset-password.html | 2 +- 6 files changed, 66 insertions(+), 45 deletions(-) diff --git a/app/utils.php b/app/utils.php index ed221a4..a087f66 100644 --- a/app/utils.php +++ b/app/utils.php @@ -38,10 +38,10 @@ function htmlToPlain($message): string { return $messageNew; } -function sendMail($message, $subject, $title, $preheader): bool { - include_once('../lib/phpmailer/Exception.php'); - include_once('../lib/phpmailer/PHPMailer.php'); - include_once('../lib/phpmailer/SMTP.php'); +function sendMail($email, $message, $subject, $title, $preheader): bool { + include_once('lib/phpmailer/Exception.php'); + include_once('lib/phpmailer/PHPMailer.php'); + include_once('lib/phpmailer/SMTP.php'); include_once('app/HTML.php'); global $RUNTIME; @@ -53,6 +53,7 @@ function sendMail($message, $subject, $title, $preheader): bool { $mailer->Port = $RUNTIME['SMTP']['PORT']; $mailer->Username = $RUNTIME['SMTP']['ADDRESS']; $mailer->Password = $RUNTIME['SMTP']['PASS']; + $mailer->SMTPAuth = true; $mailer->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS; $mailer->setFrom($RUNTIME['SMTP']['ADDRESS'], $RUNTIME['SMTP']['NAME']); @@ -61,7 +62,7 @@ function sendMail($message, $subject, $title, $preheader): bool { $mailer->isHTML(true); $mailer->Subject = $subject; $mailHtml = new HTML(); - $mailHtml->importHTML("email.html"); + $mailHtml->importHTML("mail.html"); $mailHtml->setHTMLTitle($title); $mailHtml->ReplaceLayoutInhalt('%%MESSAGE%%', $message); $mailHtml->ReplaceLayoutInhalt('%%PREHEADER%%', $preheader); diff --git a/pages/forgot.php b/pages/forgot.php index 940ee1c..3ce4c7a 100644 --- a/pages/forgot.php +++ b/pages/forgot.php @@ -15,21 +15,25 @@ if(!$validator->isValid($_POST)) { $HTML->ReplaceLayoutInhalt('%%MESSAGE%%', 'Bitte gebe deinen Benutzernamen (Vor- und Nachname) und die dazugehörige E-Mail-Adresse ein'); $HTML->ReplaceLayoutInhalt('%%MESSAGECOLOR%%', 'red'); + $HTML->build(); + echo $HTML->ausgabe(); } else { $nameParts = explode(" ", $_POST['username']); $email = strtolower(trim($_POST['email'])); - $getAccount = $RUNTIME['pdo']->prepare('SELECT Email,FirstName,LastName,PrincipalID FROM UserAccounts WHERE FirstName = ? AND LastName = ? AND Email = ?'); + $getAccount = $RUNTIME['PDO']->prepare('SELECT Email,FirstName,LastName,PrincipalID FROM UserAccounts WHERE FirstName = ? AND LastName = ? AND Email = ?'); $getAccount->execute([trim($nameParts[0]), trim($nameParts[1]), $email]); $validRequest = $getAccount->rowCount() == 1; + $uuid; + $name; if($res = $getAccount->fetch()) { $email = $res['Email']; $uuid = $res['PrincipalID']; $name = $res['FirstName'].' '.$res['LastName']; } - foreach($blockedDomains as $domain) { + foreach($RUNTIME['RESET_BLOCKED_DOMAINS'] as $domain) { if(str_ends_with($email, $domain)) { $validRequest = false; } @@ -42,7 +46,7 @@ fastcgi_finish_request(); if($validRequest) { - $getReqTime = $RUNTIME['pdo']->prepare('SELECT RequestTime FROM PasswordResetTokens WHERE PrincipalID=?'); + $getReqTime = $RUNTIME['PDO']->prepare('SELECT RequestTime FROM PasswordResetTokens WHERE PrincipalID=?'); $getReqTime->execute([$uuid]); if(($res = $getReqTime->fetch()) && time() - $res['RequestTime'] < 900) { return; @@ -50,10 +54,10 @@ require_once 'app/utils.php'; $token = generateToken(32); - $setToken = $RUNTIME['pdo']->prepare('REPLACE INTO PasswordResetTokens(PrincipalID,Token,RequestTime) VALUES(?,?,?)'); + $setToken = $RUNTIME['PDO']->prepare('REPLACE INTO PasswordResetTokens(PrincipalID,Token,RequestTime) VALUES(?,?,?)'); $setToken->execute([$uuid, $token, time()]); - sendMail(str_replace('%%NAME%%', $name, str_replace('%%RESET_LINK%%', 'https://'.$RUNTIME['DOMAIN'].'/index.php?page=reset-password&token='.$token, MESSAGE)), "Zurücksetzung des Passworts für ".$name, 'Dein Passwort zurücksetzen', 'Folge diesen Anweisungen, um ein neues Passwort für deinen 4Creative-Account festzulegen'); + sendMail($email, str_replace('%%NAME%%', $name, str_replace('%%RESET_LINK%%', 'https://'.$RUNTIME['DOMAIN'].'/index.php?page=reset-password&token='.$token, MESSAGE)), "Zurücksetzung des Passworts für ".$name, 'Dein Passwort zurücksetzen', 'Folge diesen Anweisungen, um ein neues Passwort für deinen 4Creative-Account festzulegen'); } } } diff --git a/pages/reset-password.php b/pages/reset-password.php index 0d0b30d..6603b00 100644 --- a/pages/reset-password.php +++ b/pages/reset-password.php @@ -1,19 +1,27 @@
das Passwort für deinen 4Creative-Account wurde soeben über die Funktion "Passwort vergessen" geändert.

Solltest du diese Änderung nicht selbst durchgeführt haben, wende dich bitte umgehend per E-Mail (info@4creative.net) oder Discord (@ikeytan) an uns.'; + const TOKEN_INVALID = 'Dieser Link zur Passwortzurücksetzung ist nicht gültig. Bitte klicke oder kopiere den Link aus der E-Mail, die du erhalten hast.'; + const TOKEN_EXPIRED = 'Dein Link zur Passwortzurücksetzung ist abgelaufen. Klicke hier, um eine neue Anfrage zu senden.'; - function displayTokenError() { + function displayTokenError($message) { $HTML = new HTML(); $HTML->importHTML("error.html"); - $HTML->ReplaceLayoutInhalt('%%MESSAGE%%', 'Dieser Link zur Passwortzurücksetzung ist nicht gültig. Bitte klicke oder kopiere den Link aus der E-Mail, die du erhalten hast.'); + $HTML->ReplaceLayoutInhalt('%%MESSAGE%%', $message); $HTML->build(); + echo $HTML->ausgabe(); exit(); } function displayPage($err) { + if(!isset($_GET['token']) || !preg_match('/^[a-z0-9A-Z]{32}$/', $_GET['token'])) { + displayTokenError(TOKEN_INVALID); + } + $HTML = new HTML(); $HTML->setHTMLTitle(""); $HTML->importHTML("reset-password.html"); $HTML->ReplaceLayoutInhalt('%%MESSAGE%%', $err); + $HTML->ReplaceLayoutInhalt('%%RESET_TOKEN%%', htmlspecialchars($_GET['token'])); $HTML->build(); echo $HTML->ausgabe(); exit(); @@ -36,16 +44,29 @@ displayPage('Dein Passwort muss mindestens '.$RUNTIME['PASSWORD_MIN_LENGTH'].' Zeichen lang sein.'); } - $getUuid = $RUNTIME['PDO']->prepare('SELECT PrincipalID,FirstName,LastName FROM PasswordResetTokens JOIN UserAccounts ON PasswordResetTokens.PrincipalID = PasswordResetTokens.PrincipalID WHERE Token = ?'); - if($getUuid->rowCount() == 0) { - displayTokenError(); + $getReq = $RUNTIME['PDO']->prepare('SELECT UserAccounts.PrincipalID AS UUID,FirstName,LastName,Email,Token,RequestTime FROM PasswordResetTokens JOIN UserAccounts ON UserAccounts.PrincipalID = PasswordResetTokens.PrincipalID WHERE Token = ?'); + $getReq->execute([$_POST['resetToken']]); + if($getReq->rowCount() == 0) { + displayTokenError(TOKEN_INVALID); } - $res = $getUuid->fetch(); - $uuid = $res['PrincipalID']; + $res = $getReq->fetch(); + + if(!hash_equals($res['Token'], $_POST['resetToken'])) { + displayTokenError(TOKEN_INVALID); + } + + $uuid = $res['UUID']; $name = $res['FirstName'].' '.$res['LastName']; - $getToken = $RUNTIME['PDO']->prepare('DELETE FROM PasswordResetTokens WHERE Token = ?'); - $getToken->execute([$_POST['resetToken']]); + $getToken = $RUNTIME['PDO']->prepare('DELETE FROM PasswordResetTokens WHERE PrincipalID = ? AND Token = ?'); + $getToken->execute([$uuid, $_POST['resetToken']]); + if($getToken->rowCount() == 0) { + displayTokenError(TOKEN_INVALID); + } + + if(time() - $res['RequestTime'] > 86400) { + displayTokenError(TOKEN_EXPIRED); + } $salt = bin2hex(random_bytes(16)); $hash = md5(md5(trim($_POST['password'])).':'.$salt); @@ -57,7 +78,7 @@ $_SESSION['loginMessageColor'] = 'darkgreen'; require_once 'app/utils.php'; - sendMail(str_replace('%%NAME%%', $name, MESSAGE), 'Passwort für '.$name.' zurückgesetzt', 'Passwort geändert', 'Das Passwort für deinen 4Creative-Account wurde soeben zurückgesetzt'); + sendMail($res['Email'], str_replace('%%NAME%%', $name, MESSAGE), 'Passwort für '.$name.' zurückgesetzt', 'Passwort geändert', 'Das Passwort für deinen 4Creative-Account wurde soeben zurückgesetzt'); header('Location: index.php?page=login'); exit(); @@ -65,8 +86,4 @@ } displayPage(''); - - if(!isset($_GET['token']) || !preg_match('/^[a-z0-9A-Z]{32}$/', $_GET['token'])) { - displayTokenError(); - } ?> \ No newline at end of file diff --git a/templates/forgot.html b/templates/forgot.html index 71489a8..2315f1f 100644 --- a/templates/forgot.html +++ b/templates/forgot.html @@ -22,7 +22,7 @@
-
+ Passwort vergessen @@ -32,7 +32,7 @@
- +
diff --git a/templates/mail.html b/templates/mail.html index 232d9d6..7658e03 100644 --- a/templates/mail.html +++ b/templates/mail.html @@ -1,10 +1,9 @@ - - - - - - - - - - -
-
- Logo -

%%EchoTitle%%

+ + + + +
+
+ Logo +

%%EchoTitle%%

-
+
%%MESSAGE%%
-
- +
+ \ No newline at end of file diff --git a/templates/reset-password.html b/templates/reset-password.html index 97277bf..6a1b282 100644 --- a/templates/reset-password.html +++ b/templates/reset-password.html @@ -22,7 +22,7 @@
- + Neues Passwort festlegen