javascriptphpmysqlsession-variablesuser-variables

Sending variable from $_SESSION, through AJAX to PHP for password change, how to do it?


I am making an option in my web application for users which are already logged in to change their password (so this is not a Forgot password option). I have an HTML form, then external JS file for form validation, which, if entered data pass validation, through AJAX send entered values into PHP script, which then also validate data and if everything is ok, update the password in the database. What I am having a problem now, is how to pass user ID from $_SESSION variable to the PHP script which by user ID found the row in the database of users and change password. I search the internet and I didn't find a good answer to my problem. I am trying to avoid <input type="hidden" value="<?php echo $_SESSION["userid"]?>"> method because it lacks security.

Here is my script:

HTML

<?php 
    include_once "./includes/header.php";
?>

<!-- MAIN PAGE CONTENT -->

    <main>
        <div class="main-content">
            <div class="page-title">
                <h1>Change password</h1>
            </div>
            <div class="page-content">
                <div class="page-subcontent-wrapper">
                    <div class="page-subcontent-section">
                        <form id="js-change-password-form" class="change-user-password-wrapper" method="POST" action="" autocomplete="off" accept-charset="UTF-8" novalidate>
                            <div class="change-user-password">
                                <div class="change-user-password-element">
                                    <label for="js-change-password-input">New password:</label>
                                    <div id="js-change-password-display" class="password-display hide-password"></div>
                                </div>
                                <div class="change-password-input-wrapper">
                                    <input id="js-change-password-input" type="password">
                                    <div id="js-change-password-message" class="validation-message-div"></div>
                                </div>
                            </div>
                            <div class="change-user-password">
                                <div class="change-user-password-element">
                                    <label for="js-change-password-confirm-input">Confirm password:</label>
                                    <div id="js-change-password-confirm-display" class="password-display hide-password"></div>
                                </div>
                                <div class="change-password-input-wrapper">
                                    <input id="js-change-password-confirm-input" type="password">
                                    <div id="js-change-password-confirm-message" class="validation-message-div"></div>
                                </div>
                            </div>
                            <div class="change-user-password-button">
                                <button id="js-change-password-button" type="submit">Submit</button>
                            </div>
                        </form>
                        <div class="change-user-password">
                            <div class="change-user-password-message-success">
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <script src="./js/change_password.js">
        </script>
    </main>

<?php 
    include_once "./includes/footer.php";
?>

NOTE: header.php has session_start(); on its top.

Javascript / JQuery

$(document).ready(function(){

    // Password validators

    $("#js-change-password-form").submit(function(e){

        var changePasswordValue = $("#js-change-password-input").val();
        var changePasswordValueLength = changePasswordValue.length;
        var changePasswordConfirmValue = $("#js-change-password-confirm-input").val();
        var userId = $("#js-user-id-input").val();

        e.preventDefault();
        if (changePasswordValueLength < 8) {
            var errorMessage = "<div class='error-message error-pointer'><p>Password should have at least 8 characters.</p></div>";
            $("#js-change-password-message").html(errorMessage);
            $(".validation-message-div").css("display", "flex");
            $("#js-change-password-input").css("border-color", "rgb(190,115,110)");
        } else {
            var errorMessage = "<div class='success-message'><p>Looking good!</p></div>";
            $("#js-change-password-message").html(errorMessage);
            $(".validation-message-div").css("display", "flex");
            $("#js-change-password-input").css("border-color", "rgb(95,160,95)");
            noErrorChangePassword = true;
        }

        if (changePasswordValueLength < 8) {
            $("#js-change-password-confirm-message").css("display", "none");
            $("#js-change-password-confirm-message").empty();
            $("#js-change-password-confirm-input").css("border-color", "rgb(225,225,225)");
        } else {
            if (changePasswordConfirmValue != changePasswordValue) {
                var errorMessage = "<div class='error-message error-pointer'><p>Password doesn't match.</p></div>";
                $("#js-change-password-confirm-message").html(errorMessage);
                $(".validation-message-div").css("display", "flex");
                $("#js-change-password-confirm-input").css("border-color", "rgb(190,115,110)");
            } else {
                var errorMessage = "<div class='success-message'><p>Password match.</p></div>";
                $("#js-change-password-confirm-message").html(errorMessage);
                $(".validation-message-div").css("display", "flex");
                $("#js-change-password-confirm-input").css("border-color", "rgb(95,160,95)");
                noErrorChangePasswordConfirm = true;
            }
        }

        if (noErrorChangePassword == true && noErrorChangePasswordConfirm == true) {
            $("#js-change-password-form").find("input, select, button").prop("disabled", true);
            setTimeout(function(){
                $("#js-change-password-form").fadeOut(750);
                setTimeout(function(){
                    $(".change-user-password-message-success").fadeIn(750);
                    setTimeout(function(){
                        window.location.replace('./profile.php');
                    }, 5000);
                }, 750);
            }, 500);
            $.ajax({
                url : "./includes/change_password.script.php",
                method : "POST",
                data : {
                    changePasswordValue : changePasswordValue,
                    changePasswordConfirmValue : changePasswordConfirmValue,
                    /* userId : userId, ? */
                    submit : 1
                },
                success : function(data) {
                  $(".change-user-password-message-success").html(data);
                }
            });
        }

    });

/* ---------------------------------------------------------
    -------------------- PASSWORD DISPLAY ------------------
    ----------------------------------------------------- */

    /* -------- Password Display Hidden -------- */

    $("#js-change-password-display").click(function(){
        var fieldType = $("#js-change-password-input").attr("type");
        if (fieldType == "password") {
            $("#js-change-password-input").attr("type", "text");
            $("#js-change-password-display").removeClass("hide-password").addClass("show-password");
        } else {
            $("#js-change-password-input").attr("type", "password");
            $("#js-change-password-display").removeClass("show-password").addClass("hide-password");
        }
    });

    /* -------- Confirm Password Display Hidden -------- */

    $("#js-change-password-confirm-display").click(function(){
        var fieldType = $("#js-change-password-confirm-input").attr("type");
        if (fieldType == "password") {
            $("#js-change-password-confirm-input").attr("type", "text");
            $("#js-change-password-confirm-display").removeClass("hide-password").addClass("show-password");
        } else {
            $("#js-change-password-confirm-input").attr("type", "password");
            $("#js-change-password-confirm-display").removeClass("show-password").addClass("hide-password");
        }
    });

});

PHP

<?php 
if (isset($_POST["submit"])) {
    include_once "dbconn.script.php";

    $user_id = /* ? */;
    $changePasswordValue = $_POST["changePasswordValue"];
    $changePasswordConfirmValue = $_POST["changePasswordConfirmValue"];
    $changePasswordValueLength = strlen($changePasswordValue);

    if ($changePasswordValueLength < 8) {
        echo "<p>Password should have at least 8 characters.<p>";
    } else {
        if ($changePasswordConfirmValue != $changePasswordValue) {
            echo "<p>Passwords you entered does not match.</p>";
        } else {
            $hashedChangePassword = password_hash(base64_encode(hash('sha384', $changePasswordValue, true)), PASSWORD_BCRYPT, ["cost" => 11]);
            $sql = "UPDATE hgs_users SET user_pw = ? WHERE user_id = ?";
            $stmt = mysqli_stmt_init($conn);
            if (!mysqli_stmt_prepare($stmt, $sql)) {
                echo "<p>SQL statement failed!</p>";
                exit();
            } else {
                mysqli_stmt_bind_param($stmt, "ss", $hashedChangePassword, $user_id);
                mysqli_stmt_execute($stmt);
                mysqli_stmt_close($stmt);
                echo "<p>Success!</p>";
            }
        }
    }
    mysqli_close($conn);
} else {
    header("Location: ../index.php");
}

?>

Solution

  • The question lacks the description of the environment where the password is changed.

    If you are changing the password for an already authenticated user, it should be simple as

    $user_id = $_SESSION['user_id'];

    because the user is already signed in, so you must already have his id somewhere in the session. It's up to you to store it in the session naming it whatever you want after a successful login.

    You don't need to pass the user id to the HTML form, you have it in the session already.

    If you are changing the password for a user that is NOT authenticated, it is a completely different story.

    Nowadays many websites send an HTTP link via e-mail to access the "change password" page. In the e-mail there is a special link to reset the password; the link includes a temporary UUID (that expires in a timeframe of your choice) generated for the specific password request the user made. The UUID is stored in the DB along with the user_id of the user that has requested the password reset.

    When the user clicks on the link is sent to your page that receives the UUID. Thanks to the UUID you can retrieve the user_id who has requested the password reset and do the job, deleting the UUID immediately after the password has been reset.

    Of course, there are other ways to implement a password reset mechanism but, IMHO, this is the most straightforward and common on the net.