Tom8001 Posted October 20, 2015 Share Posted October 20, 2015 Hi, how can i create a Forgotten password script? I know security can be a real issue with this if the code isn't written correctly. Quote Link to comment Share on other sites More sharing options...
benanamen Posted October 20, 2015 Share Posted October 20, 2015 (edited) One if the most important things is that passwords, rather, password hashes are properly created and stored in the DB as such. A forgot password is really a password reset since there is no way of knowing what the password is. Never ever store a password as plain text. If you are current on your PHP version >=5.50 you can use password_hash and password-verify Search this forum. Excellent answers to this question have already been posted. Edited October 20, 2015 by benanamen 1 Quote Link to comment Share on other sites More sharing options...
Tom8001 Posted October 20, 2015 Author Share Posted October 20, 2015 One if the most important things is that passwords, rather, password hashes are properly created and stored in the DB as such. A forgot password is really a password reset since there is no way of knowing what the password is. Never ever store a password as plain text. If you are current on your PHP version >=5.50 you can use password_hash and password-verify Search this forum. Excellent answers to this question have already been posted. Thanks, I will take a look at other threads. Quote Link to comment Share on other sites More sharing options...
Barand Posted October 20, 2015 Share Posted October 20, 2015 Here's one to start with http://forums.phpfreaks.com/topic/293179-reseting-of-passwords/?do=findComment&comment=1500065 Quote Link to comment Share on other sites More sharing options...
Tom8001 Posted October 20, 2015 Author Share Posted October 20, 2015 I am currently using password_hash and password_verify in my code, I am unsure can i decrypt the password? Quote Link to comment Share on other sites More sharing options...
Tom8001 Posted October 20, 2015 Author Share Posted October 20, 2015 I was thinking to just reset the password when the form is submitted and then query the database for the new password, decrypt it and send it via email? Quote Link to comment Share on other sites More sharing options...
requinix Posted October 20, 2015 Share Posted October 20, 2015 You cannot "decrypt" the password. Read those other threads. Quote Link to comment Share on other sites More sharing options...
Jacques1 Posted October 20, 2015 Share Posted October 20, 2015 (edited) You cannot “decrypt” a hash, because hashing is not encryption. Actually, the whole point of hashing passwords is that it's practically impossible to retrieve the password (assuming it's sufficiently strong). If anybody could just turn the hash back into the password, the whole exercise would be rather useless, don't you thin? So, no, you can't send people their old password. But you can reset the password: Generate a sufficiently long random number (at least 16 bytes) using openssl_random_pseudo_bytes() or mcrypt_create_iv(). Hash the number with something like SHA-2 and store the hash in the database. Send the number to the user by e-mail, embedded into a password reset link. For example https://www.yoursite.com/reset_password.php?token=01b81eb4247dd47bb554537db0c500a4 In the password reset script, you check if the random number is valid, and if it is, the user may change their password. Edited October 20, 2015 by Jacques1 1 Quote Link to comment Share on other sites More sharing options...
Tom8001 Posted October 20, 2015 Author Share Posted October 20, 2015 (edited) What i don't understand is when they request to reset their password, I will be hashing the password using password_hash but how am i ment to let them see the password in the email in plaint text? Edit: Sorry we posted at the same time Edited October 20, 2015 by Tom8001 Quote Link to comment Share on other sites More sharing options...
Jacques1 Posted October 20, 2015 Share Posted October 20, 2015 You don't let them see any plaintext password. Never send plaintext passwords. Use a temporary random reset token as described above. With this token, the user can choose a new password. 3 Quote Link to comment Share on other sites More sharing options...
Tom8001 Posted October 22, 2015 Author Share Posted October 22, 2015 Sorry i'm still fairly new to PHP i don't understand what to do about the token am i ment to use the rand() function? Quote Link to comment Share on other sites More sharing options...
benanamen Posted October 22, 2015 Share Posted October 22, 2015 @Jacques1 already told you want to use to generate the token. Re-read his post. DO NOT USE rand! 1 Quote Link to comment Share on other sites More sharing options...
Tom8001 Posted October 22, 2015 Author Share Posted October 22, 2015 Hi, This is my forgot password code so far. <?php require('./includes/connect.php'); error_reporting(E_ALL | E_NOTICE); ini_set('display_errors', 1); if($_SERVER['REQUEST_METHOD'] == "POST") { $email = $_POST['email']; $email = htmlentities($email, ENT_QUOTES); $stmt = $handler->prepare("SELECT email FROM users WHERE email = :email"); $stmt->bindParam(':email', $email, PDO::PARAM_STR, 255); $stmt->execute(); if($stmt) { $fetch = $stmt->fetch(); if($email == $fetch['email']) { $stmt = $handler->prepare("SELECT username FROM users WHERE email = :email"); $stmt->bindParam(':email', $email, PDO::PARAM_STR, 255); $stmt->execute(); $row = $stmt->fetch(); $username = $row['username']; $token = mcrypt_create_iv(MCRYPT_RAND); $headers = "Password Reset"; $body = "Hi, ".$username."!, You have recently requested to reset your password. ".PHP_EOL." \n If you did not make this request please forget this email. ".PHP_EOL." To reset your password please click this link: <a href='http://ps3modding.co.uk/forgot_password.php?token=$token'></a>"; } else { echo "The E-Mail Address entered was Not Found."; } } } ?> What i am wondering is because your not ment to store the token in the database how do you check to see if it is valid? Is it done by $_COOKIE?, Thanks Quote Link to comment Share on other sites More sharing options...
benanamen Posted October 22, 2015 Share Posted October 22, 2015 (edited) Who told you not to store the token (Hash) in the database? What are you going to compare the users token to? Edited October 22, 2015 by benanamen Quote Link to comment Share on other sites More sharing options...
Tom8001 Posted October 22, 2015 Author Share Posted October 22, 2015 I read it on a stack overflow thread somewhere, And i don't know the token is what doesn't make sense to me. Quote Link to comment Share on other sites More sharing options...
Jacques1 Posted October 22, 2015 Share Posted October 22, 2015 You're not supposed to store the plaintext token, but you do store its SHA-256 hash. To check if a token is valid, you hash it and search that hash in the database. Generating a token: <?php // generate 16 random bytes $rawToken = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM); // encode the random bytes and send the result to the user $encodedToken = bin2hex($rawToken); // hash the random bytes and store this hash in the database $tokenHash = hash('sha256', $rawToken); Looking up a token: <?php $encodedToken = $_GET['token']; // decode the token and hash it $rawToken = hex2bin($encodedToken); $tokenHash = hash('sha256', $rawToken); // now look up $tokenHash Quote Link to comment Share on other sites More sharing options...
Tom8001 Posted October 23, 2015 Author Share Posted October 23, 2015 $encodedToken = $_GET['token']; $token = hex2bin($encodedToken); $tokenHash = hash('sha256', $token); $username = $_GET['s']; $stmt = $handler->prepare("UPDATE users SET reset = ".$tokenHash." WHERE username = :u"); $stmt->bindParam(':u', $username, PDO::PARAM_STR, 255); $stmt->execute(); Fatal error: Call to a member function prepare() on a non-object I get this error when clicking the reset link in the email, it says on line 10 which is the update query Quote Link to comment Share on other sites More sharing options...
Jacques1 Posted October 23, 2015 Share Posted October 23, 2015 (edited) Your $handler variable is either not defined at all or pointing to something other than a PDO instance. Also, don't insert $tokenHash directly into the query string. You already have a prepared statement, so use a second parameter for the hash. Edited October 23, 2015 by Jacques1 Quote Link to comment Share on other sites More sharing options...
Tom8001 Posted October 23, 2015 Author Share Posted October 23, 2015 (edited) I don't see what you mean about not inserting the token hash in the query string? Edited October 23, 2015 by Tom8001 Quote Link to comment Share on other sites More sharing options...
Jacques1 Posted October 23, 2015 Share Posted October 23, 2015 I mean this: UPDATE users SET reset = ".$tokenHash." WHERE username = :u ^^^^^^^^^^^^^^ You should do this: UPDATE users SET reset = :hash WHERE username = :u ^^^^^ Quote Link to comment Share on other sites More sharing options...
Tom8001 Posted October 23, 2015 Author Share Posted October 23, 2015 (edited) Thanks, the password is able to be reset now, but i have a field in the database called 'hash' and i have the query to update it with the hashed token but it does not change, Here is my new updated code: <?php require('./includes/connect.php'); $encodedToken = $_GET['token']; $token = hex2bin($encodedToken); $tokenHash = hash('sha256', $rawToken); $username = $_GET['s']; $stmt = $handler->prepare("UPDATE users SET hash = :hash WHERE username = :u"); $stmt->bindParam(':u', $username, PDO::PARAM_STR, 255); $stmt->bindParam(':hash', $tokenHash, PDO::PARAM_STR, 255); $stmt->execute(); if($stmt) { echo ' <form action="" method="POST"> <h3>New Password: </h3> <input type="password" name="newpass" placeholder="New Password" required /><br> <h3>Confirm Password: </h3> <input type="password" name="confpass" placeholder="Confirm Password" required /><br> <input type="submit" name="update" value="Update Password"> </form> '; } else { echo "Invalid token"; exit; } if($_POST['update']) { $newpass = $_POST['newpass']; $confpass = $_POST['confpass']; if($confpass == $newpass) { $enc_password = password_hash($confpass, PASSWORD_BCRYPT); $stmt = $handler->prepare("UPDATE users SET password = :cpass WHERE username = :u"); $stmt->bindParam(':u', $username, PDO::PARAM_STR, 255); $stmt->bindParam(':cpass', $enc_password, PDO::PARAM_STR, 255); $stmt->execute(); if($stmt) { echo "Your password has been reset!"; echo '<meta http-equiv="refresh" content="0;login.php">'; } else { echo "Error"; exit; } } } ?> Edited October 23, 2015 by Tom8001 Quote Link to comment Share on other sites More sharing options...
Jacques1 Posted October 23, 2015 Share Posted October 23, 2015 (edited) The code makes no sense and is actually dangerous, because you allow anybody to update anybody's password and take over their account. For example, I could request the following URL: https://www.yoursite.com/reset_password.php?u=admin&token=idontcare You would change the token of “admin” to “idontcare” (which is obviously nonsensical) and allow me to set a new password for the admin account. Now I'm an admin of your site. It's really important that you understand the logic of a password reset before you write the code. Draw a diagram or write down a verbal description of the steps. Make sure you know what you're doing. I'll repeat the steps again: A visitor requests a password reset token for a particular user account. At that point, the visitor cannot change the password, because they first have to prove that they actually own the account. You generate a random token, send it to the e-mail address of the account and store a hash of the token in the database. The visitor clicks on the password reset link in e-mail. This automatically submits the token to the actual password reset page. On the password reset page, you hash the token and check if that hash is present in your database. If it is, then the visitor may change the password. So there are two steps: You generate a secret (the token) and send it to the user's e-mail account. The user can then use this secret to prove their identity and change their password. You should make two separate PHP scripts for the two steps. OK? If you're still not sure how it works, simply try out the password reset on Facebook, Amazon or whatever site you're using. Edited October 23, 2015 by Jacques1 Quote Link to comment Share on other sites More sharing options...
scootstah Posted October 23, 2015 Share Posted October 23, 2015 You're not supposed to store the plaintext token, but you do store its SHA-256 hash. Why hash it instead of just using bin2hex()? Quote Link to comment Share on other sites More sharing options...
benanamen Posted October 23, 2015 Share Posted October 23, 2015 (edited) For starters because you can use hex2bin() to un-hex it. You might as well just be storing plain text, not to mention, it doesn't encrypt anything. Edited October 23, 2015 by benanamen Quote Link to comment Share on other sites More sharing options...
scootstah Posted October 23, 2015 Share Posted October 23, 2015 (edited) Of course, but why does that matter? You're storing the same length value of the same entropy whether you hash it or not. So what is the point of hashing it? EDIT: I guess part of my question is, why not just store it plaintext? Edited October 23, 2015 by scootstah Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.