What I try to achieve I need to create a secure forgot password reset system. I am especially worrying about the security of this code and how to further improve it. Here's my code:
d32b6f67a9d2ff687d9b7d.php: maintenance placed one level above the root folder
<?php
define('WEB_MASTER_EMAIL', '[email protected]');
define('CONTACT_EMAIL', "[email protected]");
define('ERROR_LOG_EMAIL', '[email protected]');
date_default_timezone_set('UTC');
$maintenance = false;
$home = 'http://localhost/projectturmeric/';
$webpageTitle = "Project Turmeric";
$t_users = "users";
$t_activationKeys = "activation_keys";
$t_passwordReset = "password_reset_requests";
if ($maintenance) {
header('location:' . $home . 'under-maintenance.php');
exit();
}
?>
dbconnect placed one level above the root folder c6ef0226276fb386d4a7a7dcb4df38ee9df1105a2244b7bd.php:
<?php
define('DB_HOST', 'localhost');
define('DB_USERNAME', 'root');
define('DB_PASSWORD', '');
define('DB_NAME', "project_turmeric");
?>
system/utils.php utils.php: contains mostly used functions
<?php
function create_db_linkage_instance()
{
try {
$greenf = new PDO("mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8mb4;", DB_USERNAME, DB_PASSWORD);
return $greenf;
} catch (Exception $e) {
$date = '[' . date('d-M-Y h-i-s a') . ']';
$msg = $e->getMessage();
$err_msg = $date . ' | Exception Error | ' . $msg . ' | \n';
error_log($err_msg, 3, ERROR_LOG_BOOK);
// error_log("Date/time: $date, Exception Error check error log for more details", 1, WEB_MASTER_EMAIL, "Subject: Exception Error \nfrom: Error Log <" . ERROR_LOG_EMAIL . "> \r\n");
} catch (Error $e) {
$date = '[' . date('d-M-Y h-i-s a') . ']';
$msg = $e->getMessage();
$err_msg = $date . ' | Error | ' . $msg . ' | \n';
error_log($err_msg, 1, ERROR_LOG_BOOK);
// error_log("Date/time: $date, Error check error log for more details", 1, WEB_MASTER_EMAIL, "Subject: Error \nfrom: Error Log <" . ERROR_LOG_EMAIL . "> \r\n");
}
return null;
}
function findIP()
{
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
//check ip from share internet
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
//to check ip is pass from proxy
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
return $ip;
}
function sendMail($to, $name, $subject, $message): bool
{
$developmentMode = true;
$mail = new PHPMailer\PHPMailer\PHPMailer($developmentMode);
$mail->SMTPDebug = 0; // disables SMTP debug information (for testing)
$mail->IsSMTP();
if ($developmentMode) {
$mail->SMTPOptions = [
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
]
];
}
$mail->Host = "smtp.gmail.com";
$mail->SMTPAuth = true;
$mail->Username = "[email protected]";
$mail->Password = "********";
$mail->SMTPSecure = "tls";
$mail->Port = 587;
$mail->setFrom('[email protected]', 'Project Turmeric'); // sender email and name
$mail->AddAddress($to, $name);
$mail->IsHTML(true);
$mail->Subject = $subject;
$mail->Body = $message;
$mail->CharSet = "utf-8";
$mail->addReplyTo('[email protected]', 'Information');
$mail->wordwrap = 70;
if ($mail->send()) {
$mail->ClearAllRecipients();
$mail->clearAttachments();
return true;
}
$mail->ClearAllRecipients();
$mail->clearAttachments();
return false;
}
?>
users/forgot-password.php forgot-password.php: this script sends the account recovery email
<?php
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
// Maintenance
require '../../d32b6f67a9d2ff687d9b7d.php';
require '../system/utils.php';
if (isset($_SESSION['user_id'])) {
// user is already verified and logged in
// so redirect to index page
header('location:' . $home);
exit();
}
// error log
define('ERROR_LOG_BOOK', '../../error.log');
// functions
function endTheWebpage()
{
echo '</div>';
echo '</main>';
echo '</div>';
echo '<footer id="footer">';
include '../includes/footer.php';
echo '</footer>';
echo '<!-- SCRIPTS -->';
echo '<script type="text/javascript" src="..\js\jquery-3.4.1.min.js"></script>';
echo '<script type="text/javascript" src="..\css\bootstrap-4.3.1\js\bootstrap.min.js"></script>';
echo '</body>';
echo '</html>';
exit();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description" content="">
<meta name="author" content="">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="..\css\bootstrap-4.3.1\css\bootstrap.min.css" type="text/css">
<link rel="stylesheet" href="..\css\main.css" type="text/css">
<title>Forgot Password | <?php echo htmlspecialchars($webpageTitle, ENT_QUOTES); ?></title>
</head>
<body>
<header id="header">
<?php
$menu = 4; // activation.php
include '../includes/header.php';
?>
</header>
<div class="container">
<main>
<div id="reset-password-area">
<h1 class="text-center">Reset Password</h1>
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$email = isset($_POST['email']) ? filter_var($_POST['email'], FILTER_SANITIZE_EMAIL) : "";
$email = trim($email);
if (!empty($email)) {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors['email'] = 'Please enter a valid email.';
}
} else {
$errors['email'] = 'please enter your email.';
}
if (empty($errors)) {
$requestTime = date("Y-m-d H:i:s"); // current date / time
try {
require '../../c6ef0226276fb386d4a7a7dcb4df38ee9df1105a2244b7bd.php';
$greenf = create_db_linkage_instance();
// check if the user exists in the database
// if yes, pull the display name associated with that email
$qry = 'SELECT display_name FROM ' . $t_users . ' WHERE email=:email';
$stmt = $greenf->prepare($qry);
$stmt->bindParam(':email', $email, PDO::PARAM_STR);
$stmt->execute();
if ($stmt->rowcount() === 1) {
$data = $stmt->fetch(PDO::FETCH_OBJ);
$displayName = $data->display_name;
// delete the old token from the database if present
$qry = 'DELETE FROM ' . $t_passwordReset . ' WHERE email=:email';
$stmt = $greenf->prepare($qry);
$stmt->bindParam(':email', $email, PDO::PARAM_STR);
$stmt->execute();
$token = hash('sha256', mt_rand(1000000000, 9999999999) . date("Y-m-d H:i:s"));
$token_id = hash('sha256', mt_rand(1000000000, 9999999999) . date("Y-m-d H:i:s"));
$url = $home . "users/reset-password.php?id=" . $token_id . "&token=" . $token;
// hash the token to store it in the database
$hashedToken = password_hash($token, PASSWORD_DEFAULT);
$qry = 'INSERT INTO ' . $t_passwordReset . ' (email, token_id, token, request_time) VALUES (:email, :token_id, :token, :request_time)';
$stmt = $greenf->prepare($qry);
$stmt->bindParam(':email', $email, PDO::PARAM_STR);
$stmt->bindParam(':token_id', $token_id, PDO::PARAM_STR);
$stmt->bindParam(':token', $hashedToken, PDO::PARAM_STR);
$stmt->bindParam(':request_time', $requestTime, PDO::PARAM_STR);
$stmt->execute();
if ($stmt->rowcount() === 1) {
// send the password reset email
$to = $email;
$subject = "Reset Password - " . $webpageTitle;
$mailContent = file('../../reset-password-with-account-email-template.html');
$expires = date("d-m-Y H:i:s", strtotime($requestTime . " +30 minute")); // current date / time + 30 minutes
$temp = findIP();
if (filter_var($temp, FILTER_VALIDATE_IP) === false) {
// invalid ip
// set it to 0.0.0.0
$ip = "0.0.0.0";
} else {
$ip = $temp; // valid ip
}
$mailContent = str_replace("{{HomePage}}", $home, $mailContent);
$mailContent = str_replace("{{Logo}}", $home . "images/logo.png", $mailContent);
$mailContent = str_replace("{{WebpageTitle}}", $webpageTitle, $mailContent);
$mailContent = str_replace("{{Name}}", $displayName, $mailContent);
$mailContent = str_replace("{{PasswordResetLink}}", $url, $mailContent);
$mailContent = str_replace("{{Expires}}", $expires, $mailContent);
$mailContent = str_replace("{{ForgetPasswordResetLink}}", $home . "users/cancel-password-reset-request.php", $mailContent);
$mailContent = str_replace("{{IP}}", $ip, $mailContent);
$mailContent = str_replace("{{AccountRecoveryLink}}", $home . "users/forgot-password.php", $mailContent);
$mailContent = str_replace("{{ContactUs}}", $home . "contact-us.php", $mailContent);
$body = "";
if (is_array($mailContent)) {
foreach ($mailContent as $mc) {
$body .= $mc;
}
} else {
$body = $mailContent;
}
require_once '../libs/PHPMailer-master/src/Exception.php';
require_once '../libs/PHPMailer-master/src/PHPMailer.php';
require_once '../libs/PHPMailer-master/src/SMTP.php';
if (sendMail($to, $displayName, $subject, $body)) {
$stmt = $greenf = null;
echo '<p class="text-center" style="color: green;">Account recovery email sent to ' . $to . '. Please open this email to reset your password.</p>';
echo '<p class="text-center" class="m_info">If you don’t see this email in your inbox within 15 minutes, look for it in your junk mail folder. If you find it there, please mark it as “Not Junk”.</p>';
echo '<p class="text-center" class="m_info">If you are still experiencing any problems, contact <a href="../contact-us.php" target="_blank">support</a>.</p>';
} else {
$stmt = $greenf = null;
echo '<p class="text-center">The system is busy please try later.</p>';
}
endTheWebpage(); // close div, main, include footer, close body and html tags
} else {
// cannot insert the password reset token into the database
// display the error message and end the webpage
$stmt = $greenf = null;
echo '<p class="text-center">The system is busy please try later.</p>';
endTheWebpage(); // close div, main, include footer, close body and html tags
} // end of inserting password_reset_token into the database if condition block
} else {
// the email entered to reset the password is not found in the database.
// so send the account not found. Sign up instead email
$stmt = $greenf = null;
$to = $email;
$subject = "Reset Password - " . $webpageTitle;
$mailContent = file('../../reset-password-without-account-email-template.html');
$temp = findIP();
if (filter_var($temp, FILTER_VALIDATE_IP) === false) {
// invalid ip
// set it to 0.0.0.0
$ip = "0.0.0.0";
} else {
$ip = $temp; // valid ip
}
$mailContent = str_replace("{{HomePage}}", $home, $mailContent);
$mailContent = str_replace("{{Logo}}", $home . "images/logo.png", $mailContent);
$mailContent = str_replace("{{WebpageTitle}}", $webpageTitle, $mailContent);
$mailContent = str_replace("{{Email}}", $to, $mailContent);
$mailContent = str_replace("{{WebpageTitle}}", $webpageTitle, $mailContent);
$mailContent = str_replace("{{SignUpEmail}}", $home . "users/signup.php", $mailContent);
$mailContent = str_replace("{{IP}}", $ip, $mailContent);
$mailContent = str_replace("{{ContactUs}}", $home . "contact-us.php", $mailContent);
$body = "";
if (is_array($mailContent)) {
foreach ($mailContent as $mc) {
$body .= $mc;
}
} else {
$body = $mailContent;
}
require_once '../libs/PHPMailer-master/src/Exception.php';
require_once '../libs/PHPMailer-master/src/PHPMailer.php';
require_once '../libs/PHPMailer-master/src/SMTP.php';
if (sendMail($to, "", $subject, $body)) {
$stmt = $greenf = null;
echo '<p class="text-center" style="color: green;">Account recovery email sent to ' . $to . '. Please open this email to reset your password.</p>';
echo '<p class="text-center" class="m_info">If you don’t see this email in your inbox within 15 minutes, look for it in your junk mail folder. If you find it there, please mark it as “Not Junk”.</p>';
echo '<p class="text-center" class="m_info">If you are still experiencing any problems, contact <a href="../contact-us.php" target="_blank">support</a>.</p>';
} else {
$stmt = $greenf = null;
echo '<p class="text-center">The system is busy please try later.</p>';
}
endTheWebpage(); // close div, main, include footer, close body and html tags
} // end of existsInDatabase() if condition block
} catch (Exception $e) {
$date = '[' . date('d-M-Y h-i-s a') . ']';
$msg = $e->getMessage();
$err_msg = $date . ' | Exception Error | ' . $msg . ' | \n';
error_log($err_msg, 3, ERROR_LOG_BOOK);
// error_log("Date/time: $date, Exception Error check error log for more details", 1, WEB_MASTER_EMAIL, "Subject: Exception Error \nfrom: Error Log <" . ERROR_LOG_EMAIL . "> \r\n");
} catch (Error $e) {
$date = '[' . date('d-M-Y h-i-s a') . ']';
$msg = $e->getMessage();
$err_msg = $date . ' | Error | ' . $msg . ' | \n';
error_log($err_msg, 3, ERROR_LOG_BOOK);
// error_log("Date/time: $date, Error check error log for more details", 1, WEB_MASTER_EMAIL, "Subject: Error \nfrom: Error Log <" . ERROR_LOG_EMAIL . "> \r\n");
} // end of try catch blocks
} // end of empty($errors) if condition block without else block. The errors will be displayed next to the input field.
}
?>
<form id="reset-password-form" class="m_form" name="reset-password-form" method="post" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>">
<p class="text-center">Forgot your account’s password or having trouble logging into your account? Enter your email address and we’ll send you a recovery link.</p>
<div class="mb-3"></div>
<div class="row form-group">
<label class="col-sm-4 text-left col-form-label font-weight-bold" for="email">Email<span style="color: red;"> *</span></label>
<div class="col-sm-8">
<input class="form-control" id="email" type="email" name="email" required title="Enter your email" value="<?php if (isset($_POST['email'])) {
echo htmlspecialchars($_POST['email'], ENT_QUOTES);
} ?>">
<label class="error"><?php if (isset($errors['email'])) {
echo htmlspecialchars($errors['email'], ENT_QUOTES);
} ?></label>
</div>
</div>
<div class="row form-group">
<label class="col-sm-4 col-form-label text-left"></label>
<div class="col-sm-8 text-center">
<button class="btn btn-success M_button" id="reset-password-button" type="submit" name="reset-password-button">Send recovery email</button>
</div>
</div>
</form>
</div>
</main>
</div>
<footer id="footer">
<?php include '../includes/footer.php'; ?>
</footer>
<!-- SCRIPTS -->
<script type="text/javascript" src="..\js\jquery-3.4.1.min.js"></script>
<script type="text/javascript" src="..\css\bootstrap-4.3.1\js\bootstrap.min.js"></script>
</body>
</html>
users/reset-password.php reset-password.php: this script validates the token_id & token, allows the user to reset the password
<?php
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
// Maintenance
require '../../d32b6f67a9d2ff687d9b7d.php';
require '../system/utils.php';
if (isset($_SESSION['user_id'])) {
// user is already verified and logged in
// so redirect to index page
header('location:' . $home);
exit();
}
// error log
define('ERROR_LOG_BOOK', '../../error.log');
// functions
function endTheWebpage()
{
echo '</div>';
echo '</main>';
echo '</div>';
echo '<footer id="footer">';
include '../includes/footer.php';
echo '</footer>';
echo '<!-- SCRIPTS -->';
echo '<script type="text/javascript" src="..\js\jquery-3.4.1.min.js"></script>';
echo '<script type="text/javascript" src="..\css\bootstrap-4.3.1\js\bootstrap.min.js"></script>';
echo '</body>';
echo '</html>';
exit();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description" content="">
<meta name="author" content="">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="..\css\bootstrap-4.3.1\css\bootstrap.min.css" type="text/css">
<link rel="stylesheet" href="..\css\main.css" type="text/css">
<title>Reset Password | <?php echo htmlspecialchars($webpageTitle, ENT_QUOTES); ?></title>
</head>
<body>
<header id="header">
<?php
$menu = 4; // activation.php
include '../includes/header.php';
?>
</header>
<div class="container">
<main>
<div id="reset-password-area">
<h1 class="text-center">Reset Password</h1>
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$token_id = isset($_SESSION['id']) ? filter_var($_SESSION['id'], FILTER_SANITIZE_STRING) : "";
$token = isset($_SESSION['token']) ? filter_var($_SESSION['token'], FILTER_SANITIZE_STRING) : "";
} else {
$token_id = isset($_GET['id']) ? filter_var($_GET['id'], FILTER_SANITIZE_STRING) : "";
$token = isset($_GET['token']) ? filter_var($_GET['token'], FILTER_SANITIZE_STRING) : "";
$_SESSION['id'] = $token_id;
$_SESSION['token'] = $token;
}
if (!empty($token_id) && strlen($token_id) === 64 && !empty($token) && strlen($token) === 64) {
// token and token_id seems to be valid
// proceed to check inside the database
try {
require_once '../../c6ef0226276fb386d4a7a7dcb4df38ee9df1105a2244b7bd.php';
$greenf = create_db_linkage_instance();
$qry = 'SELECT * FROM ' . $t_passwordReset . ' WHERE token_id=:token_id';
$stmt = $greenf->prepare($qry);
$stmt->bindParam(':token_id', $token_id, PDO::PARAM_STR);
$stmt->execute();
if ($stmt->rowcount() === 1) {
$data = $stmt->fetch(PDO::FETCH_OBJ);
$email = htmlspecialchars($data->email, ENT_QUOTES);
$hashed_token = $data->token;
$requestTime = htmlspecialchars($data->request_time, ENT_QUOTES);
$expiresDate = date("Y-m-d H:i:s", strtotime($requestTime . " +30 minute"));
$expires = DateTime::createFromFormat("Y-m-d H:i:s", $expiresDate);
$currentDateTime = DateTime::createFromFormat("Y-m-d H:i:s", date("Y-m-d H:i:s"));
if ($currentDateTime < $expires) {
// the token is not expired
if (password_verify($token, $hashed_token)) {
// display the password reset form
?>
<form class="m_form" id="reset-password-form" name="reset-password-form" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>" method="POST">
<div class="mb-3"></div>
<?php
// validate the password submitted to reset
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$newPassword = isset($_POST['new_password']) ? $_POST['new_password'] : "";
$confirmPassword = isset($_POST['confirm_password']) ? $_POST['confirm_password'] : "";
if (!empty($newPassword)) {
if ($newPassword !== $confirmPassword) {
$errors['password'] = 'Your new password and confirm password do not match.';
} else if (!preg_match("/(?=.*[a-z])(?=.*[A-Z])(?=.*[\d]).{8,}/", $newPassword)) {
$errors['password'] = 'Password must contain at least eight characters, including at least 1 uppercase letter, one lowercase letter and 1 number.';
}
} else {
$errors['password'] = 'Please enter your new password.';
}
if (empty($errors)) {
$hashed_password = password_hash($newPassword, PASSWORD_DEFAULT);
$temp = findIP();
if (filter_var($temp, FILTER_VALIDATE_IP) === false) {
// invalid ip
// set it to 0.0.0.0
$ip = "0.0.0.0";
} else {
$ip = $temp; // valid ip
}
$qry = 'UPDATE ' . $t_users . ' SET password=:password, last_login_ip=:last_login_ip WHERE email=:email';
$stmt = $greenf->prepare($qry);
$stmt->bindParam(':password', $hashed_password, PDO::PARAM_STR);
$stmt->bindParam(':last_login_ip', $ip, PDO::PARAM_STR);
$stmt->bindParam(':email', $email, PDO::PARAM_STR);
$stmt->execute();
if ($stmt->rowcount() === 1) {
// new password updated successfully
$qry = 'DELETE FROM ' . $t_passwordReset . ' WHERE token_id=:token_id';
$stmt = $greenf->prepare($qry);
$stmt->bindParam(':token_id', $token_id, PDO::PARAM_STR);
$stmt->execute();
unset($_SESSION['id']);
unset($_SESSION['token']);
$stmt = $greenf = null;
echo '<p class="text-center" style="color: green;">Congratulations! Your password has been changed successfully. Please <a href="login.php">click here</a> to login and enjoy.';
endTheWebpage();
} else {
$stmt = $greenf = null;
echo '<p class="text-center">The system is busy please try later.</p>';
endTheWebpage();
} // end of rowcount() === 1 if condition block (Update query);
} // end of empty($errors) if condition block without else block (errors will be displayed next to the textboxes
} // end of $_SERVER['REQUEST_METHOD'] === 'POST if condition block to validate the password and update the database with the new password
?>
<p class="text-center">Please use the form below to reset your password.</p>
<div class="mb-3"></div>
<div class="row form-group">
<label for="new_password" class="col-sm-4 col-form-label text-left font-weight-bold">New password<span style="color: darkred;"> *</span></label>
<div class="col-sm-8">
<input class="form-control m_text" id="new-password" type="password" name="new_password" required value="<?php if (isset($_POST['new_password'])) {
echo htmlspecialchars($_POST['new_password']);
} ?>" pattern="(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).{8,}" title="Password must contain at least eight characters, including at least 1 uppercase letter, one lowercase letter and 1 number.">
<label class="error"><?php if (isset($errors['password'])) {
echo htmlspecialchars($errors['password'], ENT_QUOTES);
} ?>Password must contain at least eight characters, including at least 1 uppercase letter, one lowercase letter and 1 number.</label>
</div>
</div>
<div class="row form-group">
<label for="confirm_password" class="col-sm-4 col-form-label text-left font-weight-bold">Confirm password<span style="color: darkred;"> *</span></label>
<div class="col-sm-8">
<input class="form-control m_text" id="confirm-password" type="password" name="confirm_password" required value="<?php if (isset($_POST['confirm_password'])) {
echo htmlspecialchars($_POST['confirm_password']);
} ?>">
</div>
</div>
<div class="row form-group">
<label class="col-sm-4 col-form-label text-left"></label>
<div class="col-sm-8 text-center">
<button class="btn btn-success M_button" id="reset-password-button" type="submit" name="reset-password-button">Reset password</button>
</div>
</div>
</form>
<?php
} else {
// the token hash not matched with the token hash in the database
$stmt = $greenf = null;
echo '<p class="text-center" style="color: ed;">The link is invalid.</p>';
echo '<p class="text-center">Please <a href="forgot-password.php">click here</a> to get a new link to reset your password.</p>';
endTheWebpage();
}
} else {
// the token has been expired
$stmt = $greenf = null;
echo '<p class="text-center" style="color: ed;">The link has been expired.</p>';
echo '<p class="text-center">Please <a href="forgot-password.php">click here</a> to get a new link to reset your password.</p>';
endTheWebpage();
}
} else {
// no data returned from the database
$stmt = $greenf = null;
echo '<p class="text-center" style="color: red;">The link is invalid.</p>';
echo '<p class="text-center">Please <a href="forgot-password.php">click here</a> to get a new link to reset your password.</p>';
endTheWebpage();
} // end of $stmt->rowcount() === 1 if condition block ('SELECT QUERY')
} catch (Exception $e) {
$date = '[' . date('d-M-Y h-i-s a') . ']';
$msg = $e->getMessage();
$err_msg = $date . ' | Exception Error | ' . $msg . ' | \n';
error_log($err_msg, 3, ERROR_LOG_BOOK);
// error_log("Date/time: $date, Exception Error check error log for more details", 1, WEB_MASTER_EMAIL, "Subject: Exception Error \nfrom: Error Log <" . ERROR_LOG_EMAIL . "> \r\n");
} catch (Error $e) {
$date = '[' . date('d-M-Y h-i-s a') . ']';
$msg = $e->getMessage();
$err_msg = $date . ' | Error | ' . $msg . ' | \n';
error_log($err_msg, 3, ERROR_LOG_BOOK);
// error_log("Date/time: $date, Error check error log for more details", 1, WEB_MASTER_EMAIL, "Subject: Error \nfrom: Error Log <" . ERROR_LOG_EMAIL . "> \r\n");
} // end of try catch blocks
} else {
$stmt = $greenf = null;
echo '<p class="text-center" style="color: red;">The link is invalid.</p>';
echo '<p class="text-center">Please <a href="forgot-password.php">click here</a> to get a new link to reset your password.</p>';
endTheWebpage();
} // end of !empty($token_id) && strlen($token_id) === 64 && !empty($token) && strlen($token) === 64 if condition block
?>
</div>
</main>
</div>
<footer id="footer">
<?php include '../includes/footer.php'; ?>
</footer>
<!-- SCRIPTS -->
<script type="text/javascript" src="..\js\jquery-3.4.1.min.js"></script>
<script type="text/javascript" src="..\css\bootstrap-4.3.1\js\bootstrap.min.js"></script>
</body>
</html>
I am a beginner to PHP. Is this the correct way to implement password reset system? Are there any security issues in my code? If yes, what are they? How to overcome them?