Hello, i want to allow my users to reset their passwords if they forget it.
My problem is that i lose the code sent to user via email, if this is the link sent to user via email

http://localhost/citi/forgot.php?code=8b15388bc5ca7e2821ab6ce59b465f684942dd7901f4e31439575ab72028f84f

, when the user get to forgot.php and type in the new password, i need to update user account based on this code

code=8b15388bc5ca7e2821ab6ce59b465f684942dd7901f4e31439575ab72028f84f

.

But when i press the submit button, i lose this code

code=8b15388bc5ca7e2821ab6ce59b465f684942dd7901f4e31439575ab72028f84f

and my update fails.

I tryed putting it in a

$check_cod=mysql_real_escape_string($_GET['code']);
    $check_code=$_SESSION['$check_cod'];

but it is still not working.

here is my full code.




if(!empty($_GET['code']) && isset($_GET['code']))
    {
    $code=mysql_real_escape_string($_GET['code']);
    $check_cod=mysql_real_escape_string($_GET['code']);
    $check_code=$_SESSION['$check_cod'];

    $c=mysqli_query($conn,"SELECT forgetId FROM forget WHERE forgetcode='$code'");

    if(mysqli_num_rows($c) > 0)
    {


    $count_forgetId=mysqli_query($conn,"SELECT forgetId FROM forget WHERE forgetcode='$code' and statusTwo='0'"); 

    if(mysqli_num_rows($count_forgetId) == 1)
    {

    mysqli_query($conn,"UPDATE forget SET statusTwo='1' WHERE forgetcode='$code'");
    }
    else
    {
    $msg ="<h2>You have already reset your password, to  reset your password again, <a href='forgot.php'>please click here</a></h2>";
    }

    }
    else
    {
    $msg ="Wrong activation code.";
      }
    }

////////////////////////////////////////////////////////////////////////////////


    if($_SERVER["REQUEST_METHOD"] == "POST"){

        //define variables and set to empty values
        $passwordErr = $passwordconfirmErr = $passwordQErr = $passwordlenghtErr = "";
        $password = $passwordconfirm = "";


            if (empty($_POST["password"]) OR empty($_POST["passwordconfirm"])) {
              $passwordErr = "Password is required"; 
              } else {
              $password = test_input($_POST["password"]);
              }

              if (empty($_POST["passwordconfirm"])) {
              $passwordconfirmErr = "Confirm password is required";
              } else {
              $passwordconfirm = test_input($_POST["passwordconfirm"]);
              }

              //Check to make sure password and confirm password match in lenght
              if (strlen($_POST["passwordconfirm"]) < 6){

              $passwordlenghtErr = "Password must be more than 6 characters.";

              } else {
              $passwordconfirm = test_input($_POST["passwordconfirm"]);
              } 

              if($_POST['password'] != $_POST['passwordconfirm']) {

              $passwordQErr = "Passwords do not match";

              } else {
              $passwordconfirm = test_input($_POST["passwordconfirm"]);
              }
          }

            function test_input($data) {
              $data = trim($data);
              $data = stripslashes($data);
              $data = htmlspecialchars($data);
              return $data;
            }


    $msg='';


            //die($check_code); 
            if(!empty($_POST['password']) && isset($_POST['password'])){

            $password=mysql_real_escape_string($_POST['password']);
            $salt = uniqid(mt_rand()+microtime(true), true);
            $hpassword=hash('sha256',$password.$salt); // Encrypted password

            //$check_code=mysql_real_escape_string($_GET['code']);

            $count_uid = "SELECT `uid` FROM `users` WHERE forgetcode='$check_code'";
            die($count_uid);   
            $count=mysqli_query($conn, $count_uid);

                        // check code
                        if(mysqli_num_rows($count) == 1)
                        {

                            $upd = "UPDATE `users` SET password='$hpassword' WHERE forgetcode='$check_code'";

                            mysqli_query($conn,$upd);

                   }
                  }

Hi, try to append the query string to the action of the form:

<form method="post" action="receiving.php?<?php echo $_SERVER['QUERY_STRING']; ?>">

Or use an hidden input field:

<input type="hidden" name="code" value="<?php echo $_SERVER['QUERY_STRING']; ?>" />

But in this last case, then check for $_POST['code'] instead of $_GET['code'].

Or, if you're using $_SESSION:

<?php

    session_start();
    $_SESSION['code'] = $_GET['code'];

be sure to start it also in the receiving script, and check for $_SESSION['code'] instead of $_GET['code']:

<?php

    session_start();

    if(array_key_exists('code', $_SESSION))
    {
        # code here ...

This:

$check_cod=mysql_real_escape_string($_GET['code']);
$check_code=$_SESSION['$check_cod'];

is wrong, for different reasons:

  1. single quotes will not process the variable, so you don't get the value but an index key literally named $check_cod;
  2. you probably don't want to use the variable value as index key of your session, otherwise an attacker could try to pull out some information about a user session, submitting arbitrary index keys.

If you have problems, please share the form you're using to update the password. Bye!

Thank you very much cereal for your help.

This is my form, i forgot to say my form is on the same page, that is my problem...

<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">
            <div class="form-group">
              <input type="password" id="password" name="password" size="25"  class="form-control input-lg" placeholder="Enter Password">
            </div>
            <div class="form-group">
              <input type="password" id="passwordconfirm" name="passwordconfirm" size="25"  class="form-control input-lg" placeholder="Re-Enter Password">
            </div>
            <div class="form-group">
            <input type="submit" value="Reset Password" name="login" class="btn btn-default btn-lg btn-block" />
            </div>
          </form>

Ok, then try:

<?php

    # initialize the variable
    $code = '';

    # check if it exists and if it is alpha-numeric
    if(array_key_exists('code', $_GET) && ctype_alnum($_GET['code']) === true)
    {
        $code = $_GET['code'];
    }
?>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">
    <div class="form-group">
        <input type="password" id="password" name="password" size="25"  class="form-control input-lg" placeholder="Enter Password">
    </div>

    <div class="form-group">
        <input type="password" id="passwordconfirm" name="passwordconfirm" size="25"  class="form-control input-lg" placeholder="Re-Enter Password">
    </div>

    <div class="form-group">
        <input type="submit" value="Reset Password" name="login" class="btn btn-default btn-lg btn-block" />
    </div>
    <?php
        # adding hidden input $_POST['code']
        echo "<input type=\"hidden\" name=\"code\" value=\"{$code}\" />";
    ?>
</form>

Then in your script check for $_POST['code']. Otherwise append it to the action url of the form and then, while receiving the input, search for $_GET['code']:

<?php

    # initialize the variable
    $action = htmlspecialchars($_SERVER['PHP_SELF']);

    # check if $_GET['code'] exists
    if(array_key_exists('code', $_GET) && ctype_alnum($_GET['code']) === true)
    {
        $action .= '?code='. $_GET['code'];
    }

?>
<form method="post" action="<?php echo $action; ?>">
    <div class="form-group">
        <input type="password" id="password" name="password" size="25"  class="form-control input-lg" placeholder="Enter Password">
    </div>

    <div class="form-group">
        <input type="password" id="passwordconfirm" name="passwordconfirm" size="25"  class="form-control input-lg" placeholder="Re-Enter Password">
    </div>

    <div class="form-group">
        <input type="submit" value="Reset Password" name="login" class="btn btn-default btn-lg btn-block" />
    </div>
</form>

Note that by using this last method, the first part of your script, i.e.:

if(!empty($_GET['code']) && isset($_GET['code']))
{
    # code

Will always run. To avoid it, add $_SERVER['REQUEST_METHOD'] == 'GET' to the statement:

if($_SERVER['REQUEST_METHOD'] == 'GET' && isset($_GET['code']) && ! empty($_GET['code']))

Thanks alot Cereal, it worked perfectly. Am very greatfull.

I seems to have one problem though, and thats with the Encrypted password.
After wirinting the password to db, its not allowing me to login with it.
this is my Encrypting script on my forget.php page

$password=mysql_real_escape_string($_POST['password']);
$salt = uniqid(mt_rand()+microtime(true), true);
$hpassword=hash('sha256',$password.$salt); // Encrypted password

And this is the one on my signup page

            $salt = uniqid(mt_rand()+microtime(true), true);
            $password=hash('sha256',$password.$salt); // Encrypted password
            $activation=hash('sha256',$email+microtime(true).$salt); // Encrypted email+timestamp

This is my loging page,

<?php include ('database_connection.php');

if( isset($_SESSION['email']) || isset($_COOKIE['email'])) { // if session or cookie is stored
  header("Location: zalla/fbdashboard.php"); // redirect to home, no need to logged in
  exit();
} 

  if(isset($_POST['login'])) {

    $email = trim($_POST['email']);
    $password    = trim($_POST['userPassword']);

    $hashed = hash('sha256',$password); 

      //MySqli Select Query
    $query = "SELECT * FROM `users` WHERE email = '$email' AND password = '$hashed'";

    $query_users=mysqli_query($conn, $query);
    if(!$query_users) {
      $query = mysqli_query($conn, $query_users) or die (mysqli_error($conn));
    }


    if(mysqli_num_rows($query_users) == 1) { // if found the user in database, store session
      $found_user = mysqli_fetch_assoc($query_users); 
      $_SESSION['user_id']  = $found_user['uid'];
      $_SESSION['userEmail'] = $found_user['email'];
      $_SESSION['a'] = $found_user['email'];
      $_SESSION['b'] = $found_user['lastName'];

      header("Location: zalla/fbdashboard.php");
      exit();
    } else { // else user/password incorrect

      header("Location: index.php?log=error");
      exit();

      }
  } 

// Frees the memory associated with a result
$results->free();

// close connection 
//$mysqli->close();
?>

When i signup my password work fine, but when i reset my password its not working. Am abit confused...
Thanks for your time

On line 13 you're creating the hash without the salt:

$hashed = hash('sha256',$password);

So, it cannot generate the same hash, if you're going to use random salts for each user, then you must save it to the database.

Right now it probably works during registration & login because you create a cookie, and there's a check here:

if( isset($_SESSION['email']) || isset($_COOKIE['email'])) { // if session or cookie is stored
  header("Location: zalla/fbdashboard.php"); // redirect to home, no need to logged in
  exit();
}

that will redirect without matching the password.

From what you are saying, i need to pass the passkword through this

$password=mysql_real_escape_string($_POST['password']);
$hpassword=hash('sha256',$password); // Encrypted password

with out this in between

$salt = uniqid(mt_rand()+microtime(true), true);

Thanks for pointing this part out

if( isset($_SESSION['email']) || isset($_COOKIE['email'])) { // if session or cookie is stored
  header("Location: zalla/fbdashboard.php"); // redirect to home, no need to logged in
  exit();
}

Am abit confused when checking the password, surely it have to be passed via this function

$hpassword=hash('sha256',$password); // Encrypted password

or it will surly not work

$hpassword=hash('sha256',$password); // Encrypted password

Slow down there just for a minute.

Hash's are not designed to securely store passwords. At all. Even a silly salt won't do much to help the situation. You might as well use double ROT13 encryption. If you ever plan to have live users, this needs to be fixed.

Cryptographic hash functions are secure as digests. Ie, if you have a large block of data, and you want to make sure it was not modified, you can use a hash to securely figure out if it's been tampered with. They are not secure for storing passwords (and they are not intended for that purpose either!)

Use a strong kdf like 10000+ rounds of PBKDF2 perhaps, or even better, something like scrypt. Or you can try one of these algorithms: https://password-hashing.net/faq.html . Pufferfish looks fairly resonable to me, though I would probably stick with either scrypt of PBKDF2 for now.

Salt's make it harder for attackers to use precomputed lists. With a salt, each list would be needed for each password. The idea is to somehow mix the salt and the password (most kdf's do this for you), and you store the output and the salt in your database.

Then to confirm if a password is correct, you recombine it with the salt and the kdf to see if it matches.

@accra

A part Hiroshe's suggestion which I would strongly consider, to explain the pratical issue with your script, the problem is this:

$salt = uniqid(mt_rand()+microtime(true), true);
$password=hash('sha256',$password.$salt); // Encrypted password

Both in your registration and forget pages you are appending a $salt variable to the password. If you do this, then you have to save the salt value to the database, separated from the password, for example:

CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `email` varchar(255) NOT NULL,
  `password` char(64) NOT NULL,
  `salt` varchar(50) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `email` (`email`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 

At the moment in your login page you are not considering anymore the salt, which will change and if you don't save the original value created during the registration or recovery (forget page) process, then you cannot sign into the secure session.

So, when you verify the login, the query at line 16 of the login page becomes:

$query = "select * from users where email = '$email' and password = (sha2(concat('$password', salt), 256))";

Here we're using the sha2() function in MySQL which allows to use SHA-224, SHA-256, SHA-384, and SHA-512 hash functions. The first argument of sha2: is a concat() between the password in clear text and the salt column that matches with the searched email; the second argument is the hash value, which in this case is 256. So with a single query you can check if the user is allowed.

Note 1: uniquid() is predictable, read the notes here:

Note 2: you don't have to hash the password sent to the above query, send it clear to the database, and let the database match the correct salt column.

Otherwise you have to perform a select query to get the salt, use it into PHP to generate the hash and then perform a new query to see if it matches, for example (not tested):

$password = trim($_POST['password']);

# first query
$q = "select salt from users where email = '$email'";
$result = mysql_query($q);
$salt = mysql_fetch_row($result);

# generate the hash
$password = hash('sha256', $password.$salt[0]);

# second query
$query = "select * from users where email = '$email' and password = '$password'";

# ...

Docs:

Hope it helps, bye!

Thank Cereal for oyur time and help.
I tryed it and it worked pefectly like this

$password = hash('sha256', $password);

. But when i tryed it like this

$password = hash('sha256', $password.$salt[0]);

its is not working. Reason been that

$password = hash('sha256', );

hash it again and produce a diffrent result than that which is in db.

Its almost looking like it is not 'possible' that way... since i hashed the salt.

I think you mean salt should be stored as plain text? what will be the point of using salt then, if its plain?
Am abit comfused.

The salt is just an extra string used to change the output of the hash function, you can store this as a plain text string, or you can use an encrypt function like AES_ENCRYPT() in MySQL, example:

-- encrypt the salt
select HEX(AES_ENCRYPT('salt', 'encrypt key')) as encrypted_salt;
+----------------------------------+
| encrypted_salt                   |
+----------------------------------+
| CCBD9EDD8D878696FD44ED6720F7694E |
+----------------------------------+
1 row in set (0.12 sec)

-- decrypt the salt
select AES_DECRYPT(UNHEX('CCBD9EDD8D878696FD44ED6720F7694E'), 'encrypt key') as decrypted_salt;
+----------------+
| decrypted_salt |
+----------------+
| salt           |
+----------------+
1 row in set (0.00 sec)

Note that you need an encrypt/decrypt key as second argument of AES_ENCRYPT() and AES_DECRYPT(). The point is to limit damage if an hash is leaked at application level, for example: by using different salts you cannot see which users are using the same password. But if an attacker can submit arbitrary code, then you cannot do much.

For more information read these links:

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.