简体   繁体   中英

PHP - Secure Change Password Form/Method

I've made my own login system based on a Wikihow article and I want to make a change password field, but I don't know how I should be doing it. Here is what I have now:

JavaScript

function formchange(form, current, newpass) {
    // Create a new element input, this will be our hashed password field. 
    var p = document.createElement("input");
    var p2 = document.createElement("input");

    // Add the new element to our form. 
    form.appendChild(p);
    p.name = "p";
    p.type = "hidden";
    p.value = hex_sha512(current.value);

    form.appendChild(p2);
    p.name = "p2";
    p.type = "hidden";
    p.value = hex_sha512(newpass.value);

    // Make sure the plaintext password doesn't get sent. 
    current.value = "";
    newpass.value = "";

    // Finally submit the form. 
    form.submit();
}

PHP

if (isset($_POST['cpass'], $_POST['npass'])) {
    $cpass = $_POST['cpass'];
    $npass = $_POST['npass']; // The hashed password.

    echo "Here";

    $un = getUser();

    if ($cpass == $npass) {
        echo "If";
        $random_salt = hash('sha512', uniqid(mt_rand(1, mt_getrandmax()), true));
        $password = hash('sha512', $npass . $random_salt);

        // Insert the new user into the database 
                if ($insert_stmt = $mysqli->prepare("MYSQL UPDATE STATEMENT")) {
                    $insert_stmt->bind_param('p', $password);
                    $insert_stmt->bind_param('s', $random_salt);
                    echo "Binded";
                    // Execute the prepared query.
                    if (! $insert_stmt->execute()) {
                        header('Location: ../home.php?msg=1');
                    }
                }

        header('Location: ../home.php?msg=2');
    } else {
        // Failed
        header('Location: ../?error=1');
    }
} else {
    // The correct POST variables were not sent to this page. 
    echo 'Invalid Request';
}

It doesn't seem to work, it keeps going to home.php?msg=2, but the database isn't updated. I'm new to all of this, and it's one of my projects to learn it so apologies for terrible code. My MySQL statement is this:

UPDATE user_data SET user_data.password = 'p', user_data.salt = 's' WHERE username = '$un';

What you are trying to do is fundamentally flawed. An attacker could still perform a replay attack with the hash values created by your JavaScript function. Also you are only protecting the un-hashed password (but I can still look it up in a rainbow table) not the account. An attacker can use the hash values to authenticate as the user. This is because the hash values are the password.

To actually make a secure form you will need to use HTTPS and a Cross site request forgery token of some kind.

Here is an example say my name is Alice, and I am watching Bob's network traffic.

I (as Alice) see Bob submit your form, and then I see your web server respond back to Bob saying "thanks, password updated"

I (as Alice) now know Bob's password, even worse I can now send the same HTTP request Bob just sent you but now since I know Bob's password I can change the request to update the password again, to anything I want.

Your form is no more secure than sending the password un-hashed.

Your script is set up to forward to home.php?msg=2 for many conditions, including when the query succeeds.

if (! $insert_stmt->execute()) {
    header('Location: ../home.php?msg=1');
}

This will send it to home.php?msg=1 if the query fails to execute, note the ! . Since the query doesn't fail, the script continues and the next function is...

header('Location: ../home.php?msg=2');

"It doesnt seem to work, it keeps going to home.php?msg=2. "

So what do you expect the script to do? How you've designed it, it is supposed to do this.


Update on new edit

You should avoid using header location like this, otherwise you'll never be able to find your errors. You should avoid using header location at all during development, especially when a query fails.

I would enclose header location more and grab the error to see why the statement failed. I don't ever use mysqli because pdo, in my opinion, is better but I think this should work.

           if ($insert_stmt = $mysqli->prepare("MYSQL UPDATE STATEMENT")) {
                $insert_stmt->bind_param('p', $password);
                $insert_stmt->bind_param('s', $random_salt);
                echo "Binded";
                // Execute the prepared query.
                if (! $insert_stmt->execute()) {
                    die($mysqli->error);
                    // header('Location: ../home.php?msg=1');
                }
                else {
                    // Success, forward
                    header('Location: ../home.php?msg=2');
                }
            }
            else {
                die($mysqli->error);
            }

Removing the header('Location: ../home.php?msg=2'); at the bottom of this section in your original script.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM