What I did before coming here to post… I took a look at another thread posted a long while ago about creating a reset password function here and tried to use that to solve my problem but seems to be a dead thread that stopped from no where. I also tried researching myself to figure out how to do it myself but most walk throughs are used with mysqli and personally I am more familiar and like to use PDO for working with my registration system.
I currently have my page (resetpassword.php) making it so when a user inserts an email it will email their input with a link that looks identical to localhost/update_password.php?=1e95adc9836860c28642 which I want to redirect them to the site where it will let them change the password of that account’s email address.
I only have the resetpassword.php page currently done and believe that is working as it should. What I’m asking I guess is how to get each random string link like the localhost/update_password.php?=1e95adc9836860c28642 or anothe random string on the end to redirect to one page and how to use that certain code “1e95adc9836860c28642” on the end of the link(s) to tie to each account / email that the user requested the password reset from.
Thanks, but that post doesn’t really solve anything that I didn’t know otherwise. I am looking for something that can help me work with the code “1e95adc9836860c28642” and use it with the page to connect to the users email that wanted their password reset. What you linked me they’re talking more about which hash/rehash function to use for the method… If i’m missing something from there let me know but I personally don’t see anything there that explains how I can use the $pwresetcode in my code to change the password of the email it was sent to.
As I understand it (and am no expert), when you generate the code that you send to the user in your post above, you stick that code in a database table to link it to the user account, which may be a column in the users table, or an entirely separate table just for this purpose. When the user hits the link, you then do some other kind of verification perhaps, or maybe you just assume that because it’s gone to the registered email, it must be the actual user responding. But your update_password page then takes the unique id, uses that to retrieve the user id from the database, and updates the password then deletes the link between the unique id and the user so it can’t be run again.
I’ve pretty much done what @droopsnoot explained for my password reset. I’ve also added a date/time stamp when the request is made. They then have X amount of time before the link no longer works and they have to request again.
The variable just tells my script which record to look for and if it is in the specified time they can change the password.
By having the timeout it will further reduce the possibility of it being abused as even if someone got hold of the link if probably would no longer work by the time they used it. Also the reason to expire it once the new password has been set.
assuming their email has not been hacked it should be safe enough to send the link to their email as explained. If their email is hacked then it doesn’t matter anyway as whatever system your site uses someone can get a password reset.
Obviously also make sure they enter the new password twice and that they match so they don’t accidentally put something wrong as they can’t see the letters in a password field.
At the moment I am trying to go tackle this step by step and am working on what @droopsnoot mentioned:
you stick that code in a database table to link it to the user account
After hours of working around errors and figuring out how to right my query correctly I finally got it to work with my code:
try {
// insert $pwresetcode into resetpw_code in db where email = email submitted...
$email = $_POST['email'];
$pwresetcode = bin2hex($pwreset_code);
$pwcodedb = dbConnect()->prepare("UPDATE users SET resetpw_id = '$pwresetcode' WHERE email = '$email'");
$pwcodedb->bindParam(':email', $email);
$pwcodedb->bindParam(':pwresetcode', $pwresetcode);
$pwcodedb->execute();
} catch(PDOException $e) {
echo $pwcodedb . "<br />" . $e->getMessage();
}
The code now inserts into database in the accounts resetpw_id field accordingly to whatever email was posted in the form.
The issue I am running into now is how I can incorporate the pwresetcode into the page the user is emailed to change that specific user’s password after he inputs the new password in the next page
$uid = $_GET['uid'];
prepare("select email, username, whatever from users where resetpw_id= :uid");
execute();
// retrieve user information
// display it, prompt for new PW, etc.
// submit form
Then pass the UID and the new password to whatever processes your form submit, do the same retrieval by uid again, and as long as it’s still a valid uid, reset the password and clear the resetpw-id column.
Awesome, thank you so much for your help. After playing around some more and making sure everything works properly and was written in the correct syntax I have a function reset password function! Here is the my final code for the reset_password.php page that checks for any errors and updates the column in the database!
<?php
// include common.inc.php file
include 'core/common.inc.php';
showHeader('Update password');
// Get uid from url...
$uid = $_GET['uid'];
// echo $uid;
?>
<!-- show form if they haven't submitted -->
<form method="POST" class="">
<input type="text" name="uid" placeholder="<?php echo $uid; ?>" readonly required /><br />
<input type="password" placeholder="new password" name="newPassword" required /><br />
<input type="password" placeholder="confirm password" name="confirmNewPassword" required /><br />
<button type="submit" class="btn btn-primary pull-left">change password</button>
</form>
<?php
if (isset($uid, $_POST['newPassword'], $_POST['confirmNewPassword'])) {
$newPassword = $_POST['newPassword'];
$confirmNewPassword = $_POST['confirmNewPassword'];
// if they posted their UID, new password, and confirm new password...
$reset_pw_db_select=dbConnect()->prepare("SELECT email, username, password FROM users WHERE resetpw_id = :uid");
$reset_pw_db_select->bindParam(':uid', $uid);
$reset_pw_db_select->execute();
//password_hash();
// if passwords don't match give user error
if ($newPassword != $confirmNewPassword) {
echo '<br /><p class="reset-error">passwords do not match</p>';
// if passwords are less then 8 characters give user error
} elseif (strlen($newPassword) < 8) {
echo '<br /><p class="reset-error">passwords must be at least 8 characters</p>';
// if no errors, update passwords in db to one's posted
} else {
$hashed_password = password_hash($newPassword, PASSWORD_DEFAULT);
$reset_pw_db=dbConnect()->prepare("UPDATE users SET password = '$hashed_password' WHERE resetpw_id = '$uid'");
//echo $hashed_password;
$reset_pw_db->execute();
// provide success message and redirect to homepage
echo '<p class="reset-success">password reset successfully!</p>';
header('refresh:5;url=index.php');
}
}
?>
…but there is one last thing that I need done before I call this 100% complete. How can I make it so the uid code that is inserted into the resetpw_id column expires and returns back to null after X amount of minutes or hours? I am looking into the events portion of phpmyadmin but not completely sure how to implement what I want to do with it.
When you record the ID, set a time stamp for the entry.
Then either modify the SELECT query to select only where the time stamp is within the time scale.
Or select the time stamp field and check it against the current time, if you must tell the user it has expired.
I currently have a date_time column in my database that records the CURRENT_TIMESTAMP when the user registers. I am a bit confused as to how I SELECT the resetpw_id that is not null and UPDATE it to null again after X amount of minutes of it being stored in the database.
You don’t*. You just code the “reset” page to check the current date/time against the date/time stored in the database when you generated the reset code, and if the time is greater than the time you allow, you don’t draw the form (and maybe clear out the reset code at the same time), just draw an “expired” message, and perhaps redirect them to the page to generate a new reset link.
(* well, maybe you could, but it seems like you’d have to create a CRON job to do that, and it seems like overkill because it doesn’t matter if the link stays in the database after it expires, just that you don’t allow it to be used after it expired).
After some time I got the function to work. Here is my final code that checks if the $difference of the $current_time and $resetpw_date are greater than 15 minutes and if it does, it will provide the user an error
<?php
// testing echo to single column
$fetch_time = dbConnect()->prepare("SELECT resetpw_date FROM users WHERE resetpw_id = :uid");
$fetch_time->bindParam(':uid', $uid);
$fetch_time->execute();
while($row = $fetch_time->fetch(PDO::FETCH_ASSOC)) {
//$stored_username = $row['username'];
$stored_resetpw_date = $row['resetpw_date'];
}
// date_time of when user registered
echo $stored_resetpw_date . '<br />';
// date_time RIGHT NOW!
date_default_timezone_set('US/Eastern');
$current_time = date('Y-m-d G:i:s');
echo $current_time . '<br />';
// Difference between $stored_resetpw_date and $current_time
$current_time2 = new DateTime($current_time);
$current_time3 = new DateTime($stored_resetpw_date);
$difference = date_diff($current_time2, $current_time3);
// below format is minutes
echo $difference->format('%I:%S');
// y3s
if (isset($uid, $_POST['newPassword'], $_POST['confirmNewPassword'])) {
$newPassword = $_POST['newPassword'];
$confirmNewPassword = $_POST['confirmNewPassword'];
// if they posted their UID, new password, and confirm new password...
$reset_pw_db_select=dbConnect()->prepare("SELECT email, username, password FROM users WHERE resetpw_id = :uid");
$reset_pw_db_select->bindParam(':uid', $uid);
$reset_pw_db_select->execute();
// if passwords don't match give user error
if ($newPassword != $confirmNewPassword) {
echo '<p class="reset-error">passwords do not match</p>';
// if passwords are less then 8 characters give user error
} elseif (strlen($newPassword) < 8) {
echo '<p class="reset-error">passwords must be at least 8 characters</p>';
// if uid was made > 15:00 minutes:senconds ago....
} elseif ($difference > '15:00') {
echo '<p class="reset-error">reset uid has expired</p>';
// if no errors, update passwords in db to one's posted
} else {
$hashed_password = password_hash($newPassword, PASSWORD_DEFAULT);
$reset_pw_db=dbConnect()->prepare("UPDATE users SET password = '$hashed_password' WHERE resetpw_id = '$uid'");
//echo $hashed_password;
$reset_pw_db->execute();
// provide success message and redirect to homepage
echo '<p class="reset-success">password reset successfully!</p>';
header('refresh:5;url=index.php');
}
}
As I thought I complete the function and got it working 100%, I did not. For some reason my if statement that checks if the $difference is greater than '15:00' echo’s that the uid is expired (even if the $difference is actually less than '15:00'
Not sure what is causing this - maybe I wrote it wrong in some way? Any help would be greatly appreciated! Here is what my current code looks like:
<?php
// include common.inc.php file
include 'core/common.inc.php';
showHeader('Update password');
// Get uid from url...
$uid = $_GET['uid'];
// echo $uid;
// protect page from users already logged in...
protect();
?>
<!-- show form if they haven't submitted -->
<div class="row">
<div class="col-md-9"></div>
<div class="col-md-3">
<form method="POST" id="form-main">
<input type="text" placeholder="your uid is <?php echo $uid; ?>" name="uid" class="input2" readonly required />
<input type="password" placeholder="new password" name="newPassword" class="input" required />
<input type="password" placeholder="confirm new password" name="confirmNewPassword" class="input" required />
<br />
<button type="submit" class="btn btn-primary">change password</button>
</form>
</div>
</div>
<?php
// testing echo to single column
$fetch_time = dbConnect()->prepare("SELECT resetpw_date FROM users WHERE resetpw_id = :uid");
$fetch_time->bindParam(':uid', $uid);
$fetch_time->execute();
while($row = $fetch_time->fetch(PDO::FETCH_ASSOC)) {
//$stored_username = $row['username'];
$stored_resetpw_date = $row['resetpw_date'];
}
// date_time of when user registered
echo $stored_resetpw_date . '<br />';
// date_time RIGHT NOW!
date_default_timezone_set('US/Eastern');
$current_time = date('Y-m-d G:i:s');
echo $current_time . '<br />';
// Difference between $stored_resetpw_date and $current_time
$current_time2 = new DateTime($current_time);
$current_time3 = new DateTime($stored_resetpw_date);
$difference = date_diff($current_time2, $current_time3);
// below format is minutes:seconds
echo $difference->format('%I:%S');
// ...
if (isset($uid, $_POST['newPassword'], $_POST['confirmNewPassword'])) {
$newPassword = $_POST['newPassword'];
$confirmNewPassword = $_POST['confirmNewPassword'];
// if they posted their UID, new password, and confirm new password...
$reset_pw_db_select=dbConnect()->prepare("SELECT email, username, password FROM users WHERE resetpw_id = :uid");
$reset_pw_db_select->bindParam(':uid', $uid);
$reset_pw_db_select->execute();
// if passwords don't match give user error
if ($newPassword != $confirmNewPassword) {
echo '<p class="error">passwords do not match</p>';
// if passwords are less then 8 characters give user error
} elseif (strlen($newPassword) < 8) {
echo '<p class="error">passwords must be at least 8 characters</p>';
// if uid was made > 15:00 minutes:senconds ago....
} elseif ($difference > '15:00') {
echo '<p class="error">reset uid has expired</p>';
// if no errors, update passwords in db to one's posted
} else {
$hashed_password = password_hash($newPassword, PASSWORD_DEFAULT);
$reset_pw_db=dbConnect()->prepare("UPDATE users SET password = '$hashed_password' WHERE resetpw_id = '$uid'");
//echo $hashed_password;
$reset_pw_db->execute();
// provide success message and redirect to homepage
echo '<p class="success">password reset successfully!</p>';
header('refresh:5;url=index.php');
}
}
?>
not sure if it’s the best way to do it or not but when i deal with dates i tend to convert them to a string of numbers without any formating. So 10.30am on the 3st Jan 2016 would be 20160103103000. This means you can directly compare against other date/times in the same format using <,=,>,
<?php date('YmdHis', strtotime($storeddate) );?>
You will need to return your stored time in the database and add 15mins. Then check that the current time is less than that.
Eg if the stored time is 10am they have until 10.15am (the stored + 15) so you need to check the current time is less than 10.15.
Alright, I have tried to rework my code so that the format for when the user requested their password reset is in a numeric string and doesn’t have any sort of formatting. I amm not sure how exactly to write the code to check for 15 minutes after the stored resetpw_date. I have adjusted my code so it is now using non-formatted date so it would look something like 20161101223938
<?php
// include common.inc.php file
include 'core/common.inc.php';
showHeader('Update password');
// Get uid from url...
$uid = $_GET['uid'];
// echo $uid;
// protect page from users already logged in...
protect();
?>
<!-- show form if they haven't submitted -->
<div class="row">
<div class="col-md-9"></div>
<div class="col-md-3">
<form method="POST" id="form-main">
<input type="text" placeholder="your uid is <?php echo $uid; ?>" name="uid" class="input2" readonly required />
<input type="password" placeholder="new password" name="newPassword" class="input" required />
<input type="password" placeholder="confirm new password" name="confirmNewPassword" class="input" required />
<br />
<button type="submit" class="btn btn-primary">change password</button>
</form>
</div>
</div>
<?php
// set timezone
date_default_timezone_set('America/New_York');
// testing echo to single column
$fetch_time = dbConnect()->prepare("SELECT resetpw_date FROM users WHERE resetpw_id = :uid");
$fetch_time->bindParam(':uid', $uid);
$fetch_time->execute();
while($row = $fetch_time->fetch(PDO::FETCH_ASSOC)) {
//$stored_username = $row['username'];
$stored_resetpw_date = $row['resetpw_date'];
$resetpw_date = new DateTime($stored_resetpw_date);
}
// date_time of when user registered
echo 'user requested new password on ' . $resetpw_date->format('YmdHis') . '<br />';
// date_time RIGHT NOW!
$current_time = date('YmdHis');
echo 'it is currently ' . $current_time . ' in America/New_York<br />';
// Difference between $stored_resetpw_date and $current_time
$current_time2 = new DateTime($current_time);
$current_time3 = new DateTime($stored_resetpw_date);
$difference = date_diff($current_time2, $current_time3);
// below format is year,month,day,hour,minute,second
echo 'difference between the two are ' . $difference->format('%Y%m%d%H%i%s') . '<br />';
// ...
if (isset($uid, $_POST['newPassword'], $_POST['confirmNewPassword'])) {
$newPassword = $_POST['newPassword'];
$confirmNewPassword = $_POST['confirmNewPassword'];
// if they posted their UID, new password, and confirm new password...
$reset_pw_db_select=dbConnect()->prepare("SELECT email, username, password FROM users WHERE resetpw_id = :uid");
$reset_pw_db_select->bindParam(':uid', $uid);
$reset_pw_db_select->execute();
// if passwords don't match give user error
if ($newPassword != $confirmNewPassword) {
echo '<p class="error">passwords do not match</p>';
// if passwords are less then 8 characters give user error
} elseif (strlen($newPassword) < 8) {
echo '<p class="error">passwords must be at least 8 characters</p>';
// if uid was made > 15:00 minutes:senconds ago....
} elseif ($difference > '000001500') {
echo '<p class="error">reset uid has expired</p>';
// if no errors, update passwords in db to one's posted
} else {
$hashed_password = password_hash($newPassword, PASSWORD_DEFAULT);
$reset_pw_db=dbConnect()->prepare("UPDATE users SET password = '$hashed_password' WHERE resetpw_id = '$uid'");
//echo $hashed_password;
$reset_pw_db->execute();
// provide success message and redirect to homepage
echo '<p class="success">password reset successfully!</p>';
header('refresh:5;url=index.php');
}
}
?>
date_diff and the like are great for when you need to format the date / time difference, but when you don’t it seems actually less complex to use what I think of (perhaps incorrectly) as the older-style date / time handling functions.