-->

how can I make the password verify function work w

2019-09-22 06:29发布

问题:

I want to make an login system with password verify. I have encrypted my password with password default. I also stored the hash in the database. Now I want to use password verify to make an login system, but the function always reteruns a value of true. can someone explains why? And can someone explain me how I can use password verify with $_POST?

code for the hash

<?php
/**
 * Created by PhpStorm.
 * User: jbosma
 * Date: 24/07/2018
 * Time: 23:21
 */
include_once "dbconnection.php";
if (isset($_POST["submit"])) { // send the input
    $username = $_POST["username"]; // post the input username
    $password = $_POST["password"]; // post the input password
    $conn = new PDO("mysql:host=localhost;dbname=loginapp", $user, $pass); // database connection
    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // error mode
    $hash = PASSWORD_HASH($password, PASSWORD_DEFAULT); // make $hash so you can give that to your password in the query
    $sql = "INSERT INTO users (username, password) VALUES ('".$username."','".$hash."')"; // insert the username and the password that will be hashed
    $conn->exec($sql); // excecute the query above
    header('location: register.php'); // after you submit it redirects to login.php




}

code for password verify

<?php
/**
 * Created by PhpStorm.
 * User: jbosma
 * Date: 27/07/2018
 * Time: 12:11
 */
include_once "dbconnection.php";
if (isset($_POST["submit"])) { // send the input
    $username = $_POST["username"]; // post the input username
    $password = $_POST["password"]; // post the input password

    $hash = $password; // hash from password
    if (password_verify($_POST["password"], $hash)) {
        echo "Welcome";
    } else {
        echo "Wrong Password";
    }
}
?>

回答1:

Have something like this in your reg form, it's using BCRYPT in this instance. This is just showing the password_hash function

Register

// Other code  leading up to this..
....
//
$passHash = password_hash($pass, PASSWORD_BCRYPT, array("cost" => 12));


  $insrt = "INSERT INTO users (username, password) VALUES (:username, :password)";
  $stmt = $pdo->prepare($insrt);


  $stmt->bindValue(':username', $username);
  $stmt->bindValue(':password', $passHash);


  $result = $stmt->execute();

  if($result){
// Do whatever you want

login

// Other code  leading up to this..
....
//
$validPassword = password_verify($pass, $user['password']);

    if($validPassword){

        $_SESSION['user_id'] = $user['username'];
        $_SESSION['logged_in'] = time();

        // redirects in this case
        header( "Location: /wherever.php" );
        die();

    } else{
        die('Wrong password!');
    }

This is just showing how to use the functions themselves, apply this principle and you should be good to go



回答2:

The problem

Your password_verify returns always true because, by using password_verify($_POST["password"], $hash), you are actually comparing $_POST["password"] with itself - since $hash = $password and $password = $_POST["password"].

The solution

The password_verify should compare the posted password ($_POST["password"]) with the password hash, which has to be fetched from the database, first. So your login code should look something like this:

<?php
include_once "dbconnection.php";

if (isset($_POST["submit"])) {
    $username = $_POST["username"];
    $password = $_POST["password"];

    $sql = "SELECT password FROM users where username = '" . $username . "' LIMIT 1";
    $statement = $conn->query($sql);
    $credentials = $statement->fetch(PDO::FETCH_ASSOC);

    if ($credentials) { // Record found.
        $hash = $credentials['password'];

        // Compare the posted password with the password hash fetched from db.
        if (password_verify($password, $hash)) {
            echo "Welcome";
        } else {
            echo "Wrong Password";
        }
    } else {
        echo 'No credentials found for the given user.';
    }
}

Note that this solution is opened to sql injections. Use prepared statements instead (see the extended example bellow).


Another, extended example:

Here is another example of using the password_hash - password_verify tandem. Though I agree with @tadman: you should make use of external libraries - offered as part of a framework of your wish, or as standalone components - providing a solide security layer.

Note that the first important step in avoiding the so-called sql injections and, more specifically, in assuring a secure authentication/authorization system, is the use of prepared statements. Try to always apply this preparation step when you are working with a data access layer.

register.php

<?php
require 'connection.php';

// Signalize if a new account could be created, or not.
$accountCreated = FALSE;

/*
 * ================================
 * Operations upon form submission.
 * ================================
 */
if (isset($_POST['submit'])) {
    /*
     * =======================
     * Read the posted values.
     * =======================
     */
    $username = $_POST['username'] ?? '';
    $password = $_POST['password'] ?? '';

    /*
     * ===========================
     * Validate the posted values.
     * ===========================
     */
    // Validate the username.
    if (empty($username)) {
        $errors[] = 'Please provide a username.';
    } /* Other validations here using elseif statements */

    // Validate the password.
    if (empty($password)) {
        $errors[] = 'Please provide a password.';
    } /* Other validations here using elseif statements */

    /*
     * ==================================
     * Check if user exists. Save if not.
     * ==================================
     */
    if (!isset($errors)) {
        /*
         * =============================
         * Check if user already exists.
         * =============================
         */

        /*
         * The SQL statement to be prepared. Notice the so-called named markers.
         * They will be replaced later with the corresponding values from the
         * bindings array when using PDOStatement::bindValue.
         *
         * When using named markers, the bindings array will be an associative
         * array, with the key names corresponding to the named markers from
         * the sql statement.
         *
         * You can also use question mark markers. In this case, the bindings
         * array will be an indexed array, with keys beginning from 1 (not 0).
         * Each array key corresponds to the position of the marker in the sql
         * statement.
         *
         * @link http://php.net/manual/en/mysqli.prepare.php
         */
        $sql = 'SELECT COUNT(*)
                FROM users
                WHERE username = :username';

        /*
         * The bindings array, mapping the named markers from the sql
         * statement to the corresponding values. It will be directly
         * passed as argument to the PDOStatement::execute method.
         *
         * @link http://php.net/manual/en/pdostatement.execute.php
         */
        $bindings = [
            ':username' => $username,
        ];

        /*
         * Prepare the sql statement for execution and return a statement object.
         *
         * @link http://php.net/manual/en/pdo.prepare.php
         */
        $statement = $connection->prepare($sql);

        /*
         * Execute the prepared statement. Because the bindings array
         * is directly passed as argument, there is no need to use any
         * binding method for each sql statement's marker (like
         * PDOStatement::bindParam or PDOStatement::bindValue).
         *
         * @link http://php.net/manual/en/pdostatement.execute.php
         */
        $statement->execute($bindings);

        /*
         * Fetch the data and save it into a variable.
         *
         * @link https://secure.php.net/manual/en/pdostatement.fetchcolumn.php
         */
        $numberOfFoundUsers = $statement->fetchColumn(0);

        if ($numberOfFoundUsers > 0) {
            $errors[] = 'The given username already exists. Please choose another one.';
        } else {
            /*
             * ========================
             * Save a new user account.
             * ========================
             */
            // Create a password hash.
            $passwordHash = password_hash($password, PASSWORD_BCRYPT);

            $sql = 'INSERT INTO users (
                        username,
                        password
                    ) VALUES (
                        :username,
                        :password
                    )';

            $bindings = [
                ':username' => $username,
                ':password' => $passwordHash,
            ];

            $statement = $connection->prepare($sql);
            $statement->execute($bindings);

            // Signalize that a new account was successfully created.
            $accountCreated = TRUE;

            // Reset all values so that they are not shown in the form anymore.
            $username = $password = NULL;
        }
    }
}
?>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes" />
        <meta charset="UTF-8" />
        <!-- The above 3 meta tags must come first in the head -->

        <title>Demo - Register</title>

        <style type="text/css">
            .messages { margin-bottom: 10px; }
            .messages a { text-transform: uppercase; font-weight: 700;}
            .error, .success { margin-bottom: 5px; }
            .error { color: #ff0000; }
            .success { color: #32cd32; }
            .form-group { margin-bottom: 10px; }
            .form-group label { display: inline-block; min-width: 90px; }
        </style>
    </head>
    <body>

        <h3>
            Register
        </h3>

        <div class="messages">
            <?php
            if (isset($errors)) {
                foreach ($errors as $error) {
                    ?>
                    <div class="error">
                        <?php echo $error; ?>
                    </div>
                    <?php
                }
            } elseif ($accountCreated) {
                ?>
                <div class="success">
                    You have successfully created your account.
                    <br/>Would you like to <a href="login.php">login</a> now?
                </div>
                <?php
            }
            ?>
        </div>

        <form action="" method="post">

            <div class="form-group">
                <label for="username">Username</label>
                <input type="text" id="username" name="username" value="<?php echo $username ?? ''; ?>" placeholder="Username">
            </div>

            <div class="form-group">
                <label for="password">Password</label>
                <input type="password" id="password" name="password" value="<?php echo $password ?? ''; ?>" placeholder="Password">
            </div>

            <button type="submit" id="registerButton" name="submit" value="register">
                Register
            </button>
        </form>

    </body>
</html>

login.php

<?php
require 'connection.php';

/*
 * ================================
 * Operations upon form submission.
 * ================================
 */
if (isset($_POST['submit'])) {
    /*
     * =======================
     * Read the posted values.
     * =======================
     */
    $username = $_POST['username'] ?? '';
    $password = $_POST['password'] ?? '';

    /*
     * ===========================
     * Validate the posted values.
     * ===========================
     */
    // Validate the username.
    if (empty($username)) {
        $errors[] = 'Please provide a username.';
    } /* Other validations here using elseif statements */

    // Validate the password.
    if (empty($password)) {
        $errors[] = 'Please provide a password.';
    } /* Other validations here using elseif statements */

    /*
     * ======================
     * Check the credentials.
     * ======================
     */
    if (!isset($errors)) { // No errors yet.
        $sql = 'SELECT username, password
                FROM users
                WHERE username = :username
                LIMIT 1';

        $statement = $connection->prepare($sql);

        $statement->execute([
            ':username' => $username,
        ]);

        /*
         * Fetch the credentials into an associative array.
         * If no record is found, the operation returns FALSE.
         */
        $credentials = $statement->fetch(PDO::FETCH_ASSOC);

        if ($credentials) { // Record found.
            $fetchedUsername = $credentials['username'];
            $fetchedPasswordHash = $credentials['password'];

            /*
             * Compare the posted username with the one saved in db and the posted
             * password with the password hash saved in db using password_hash.
             *
             * @link https://secure.php.net/manual/en/function.password-verify.php
             * @link https://secure.php.net/manual/en/function.password-hash.php
             */
            if (
                    $username === $fetchedUsername &&
                    password_verify($password, $fetchedPasswordHash)
            ) {
                header('Location: welcome.html');
                exit();
            } else {
                $errors[] = 'Invalid credentials. Please try again.';
            }
        } else {
            $errors[] = 'No credentials found for the given user.';
        }
    }
}
?>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes" />
        <meta charset="UTF-8" />
        <!-- The above 3 meta tags must come first in the head -->

        <title>Demo - Login</title>

        <script type="text/javascript">
            function validateForm() {
                // ...Some form validation, if needed...
                return true;
            }
        </script>

        <style type="text/css">
            .messages { margin-bottom: 10px; }
            .error { margin-bottom: 5px; color: #ff0000; }
            .form-group { margin-bottom: 10px; }
            .form-group label { display: inline-block; min-width: 90px; }
        </style>
    </head>
    <body>

        <h3>
            Login
        </h3>

        <div class="messages">
            <?php
            if (isset($errors)) {
                foreach ($errors as $error) {
                    ?>
                    <div class="error">
                        <?php echo $error; ?>
                    </div>
                    <?php
                }
            }
            ?>
        </div>

        <form action="" method="post" onsubmit="return validateForm();">

            <div class="form-group">
                <label for="username">Username</label>
                <input type="text" id="username" name="username" value="<?php echo $username ?? ''; ?>" placeholder="Username">
            </div>

            <div class="form-group">
                <label for="password">Password</label>
                <input type="password" id="password" name="password" value="<?php echo $password ?? ''; ?>" placeholder="Password">
            </div>

            <button type="submit" id="loginButton" name="submit" value="login">
                Login
            </button>
        </form>

    </body>
</html>

connection.php

<?php

/*
 * This page contains the code for creating a PDO connection instance.
 */

// Db configs.
define('HOST', 'localhost');
define('PORT', 3306);
define('DATABASE', 'tests');
define('USERNAME', 'root');
define('PASSWORD', 'root');
define('CHARSET', 'utf8');

$connection = new PDO(
        sprintf('mysql:host=%s;port=%s;dbname=%s;charset=%s', HOST, PORT, DATABASE, CHARSET)
        , USERNAME
        , PASSWORD
        , [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_EMULATE_PREPARES => FALSE,
    PDO::ATTR_PERSISTENT => FALSE,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]
);

welcome.html

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes" />
        <meta charset="UTF-8" />
        <!-- The above 3 meta tags must come first in the head -->

        <title>Demo - Welcome</title>
    </head>
    <body>

        <h3>
            Welcome
        </h3>

        <div>
            Hi. You are now logged-in.
        </div>

    </body>
</html>

Table definition

Notice the length of 255 characters of the password column. The field should be (at least) so long. More details at password_hash (in "Description").

CREATE TABLE `users` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(100) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;