10

My registration script accepts a user's password and then uses PHP's password_hash function to encrypt the password, then places it in a database. When I go to login using the just created user, I'm getting the error that checks if the passwords are the same or not. In my case, they're not. What am I doing wrong when I make the call to the password_verify function in the login script?

REGISTER

if($_SERVER["REQUEST_METHOD"] == "POST"){
    function secure($data){
        $data = trim($data);
        $data = stripslashes($data);
        $data = htmlspecialchars($data);
        return($data);
    }

    $p_num = secure($_POST["p_number"]);
    $first_name = secure($_POST["first_name"]);
    $last_name = secure($_POST["last_name"]);
    $email = secure($_POST["email"]);
    $password = secure($_POST["pw"]);
    $verify_password = secure($_POST["pw_verify"]);
    $program = secure($_POST["program"]);
    $role = secure($_POST["role"]);
    $logged_in = 0;
    $registered = 0;
    $image = "../images/profile_placeholder.png";

    if($password != $verify_password){
        echo "Nope.  Passwords";
    }
    else{
        $registered = 1;
        $password = password_hash($password, PASSWORD_DEFAULT);
        $insert = "INSERT INTO `$user_table`(`user_id`, `first_name`, `last_name`, `password`, `image`, `email`, `program`, `role`, `logged_in`, `registered`) VALUES('" .$p_num ."', '" .$first_name ."', '" .$last_name ."', '" .$password ."', '" .$image ."', '" .$email ."', '" .$program ."', '" .$role ."', '" .$logged_in ."', '" .$registered ."')";
        $query = mysqli_query($connect, $insert);
        echo "Success!";
    }
}

LOGIN

if($_SERVER["REQUEST_METHOD"] == "POST"){
    $p_num = $_POST["username"];
    $pwd = $_POST["password"];

    $query = "SELECT * FROM `$user_table` WHERE `user_id` = '$p_num'";
    $result = mysqli_query($connect, $query);
    while($row = mysqli_fetch_assoc($result)){
        $user_id = "{$row['user_id']}";
        $first_name = "{$row['first_name']}";
        $last_name = "{$row['last_name']}";
        $user_name = $first_name ." " .$last_name;
        $password = "{$row['password']}";
        $image = "{$row['image']}";
        $email = "{$row['email']}";
        $program = "{$row['program']}";
        $role = "{$row['role']}";
        $status = "{$row['logged_in']}";
        $registered = "{$row['registered']}";
        if(($user_id == $p_num) && (password_verify($pwd, $password))){
            $_SESSION["id"] = $user_id;
            $_SESSION["user"] = $user_name;
            $_SESSION["program"] = $program;
            $_SESSION["pass"] = $password;
            $_SESSION["image"] = $image;
            $_SESSION["email"] = $email;
            $_SESSION["role"] = $role;
            $_SESSION["status"] = $status;
            $_SESSION["registered"] = $registered;
            $loggedin = "UPDATE `$user_table` SET `logged_in` = 1 WHERE `user_id` = '$user_id'";
        }
    var_dump($pwd);
    var_dump($password);
}

Here is what I get when I do var_dump:

string(1) "1" string(16) "$2y$10$0aysCso3b"

So clearly, the passwords are not being matched together. So, on the registration script, the password is hashed and sent to the database. Then, when the user goes to login, the login script looks at the password the user entered to login, and then checks it against the hashed password in the database using password_verify. Yet, the hashed password isn't accepting the un-hashed password as a match. What I'm not understanding is, why?

  • Where are `$pwd` and `$password` coming from in your login script? – Marc B Oct 23 '14 at 19:57
  • Whoops, thought I copied everything. It's been added to the question. –  Oct 23 '14 at 20:02
  • Sidenote: You should add `exit;` after each of your headers. I.e.: `header("Location: ../pages/denied_login.php"); exit;` – Funk Forty Niner Oct 23 '14 at 20:03
  • Add error reporting to the top of your file(s) right after your opening ` – Funk Forty Niner Oct 23 '14 at 20:04
  • 1
    I've got both included and it's still kickin back nothin. –  Oct 23 '14 at 20:06
  • 1
    Your `secure()` function may be altering the password hash. Use prepared statements instead. – Funk Forty Niner Oct 23 '14 at 20:09
  • 1
    @Gumbo My secure method is not what's being called into question here. I understand the queries are vulnerable to injection, as of this moment, I'm not concerned with that. –  Oct 23 '14 at 20:09
  • While storing/hashing your password it could. Use `var_dump()` to see what's passing through. – Funk Forty Niner Oct 23 '14 at 20:10
  • @Fred -ii- Sorry, that comment was to Gumbo, didn't see yours til just now –  Oct 23 '14 at 20:11
  • lol no worries, I figured as much ;) – Funk Forty Niner Oct 23 '14 at 20:12
  • Ok, so the passwords aren't actually matching. –  Oct 23 '14 at 20:16
  • Here's a method I use `$sql = "SELECT * FROM table WHERE id = 1"; $result = $mysqli->query($sql); if ($result->num_rows === 1) { $row = $result->fetch_array(MYSQLI_ASSOC); if (password_verify($pwd, $row['password'])) { echo "Match"; }` you can add the fancy stuff after ;) – Funk Forty Niner Oct 23 '14 at 20:19
  • For some reason, the passwords are still returning as different strings –  Oct 23 '14 at 20:35
  • You're not still using that `secure()` function to store passwords with, are you? I suggest you make a copy of your script, drop the function and use it normally. Create a new user/password, I.e.: `john` with `12345`, then check it again with my suggestion I've given you. – Funk Forty Niner Oct 23 '14 at 20:44
  • I deleted the secure function entirely and removed all of the calls to it. I've even created a new user with a new password. But for some reason, the password_verify function is not matching the accepted password and expected password. –  Oct 23 '14 at 20:53
  • I also added the var_dump outputs to the question, which is showing that they are not matched, but according to the documentation, they should match. –  Oct 23 '14 at 21:04
  • Wait a minute, I think your column's length isn't long enough. A hash will produce a much longer length than that. A hash will produce something like `$2y$10$fXJEsC0zWAR2tDrmlJgSaecbKyiEOK9GDCRKDReYM8gH2bG2mbO4e` and your var_dump shows as `$2y$10$0aysCso3b` - increase your column's length to `255` just to be sure and set to `VARCHAR` if not already doing so. – Funk Forty Niner Oct 23 '14 at 21:12
  • 1
    I know that it's set to VARCHAR, and I'm pretty sure the length is set to 255(it looks odd to me too that it seems to be cutting off). I'll have to verify for sure tomorrow what the length is. I'll let you know when I find out. –  Oct 23 '14 at 21:36

2 Answers2

18

Here is what I use for password_hash and password_verify. Try it out as written, you can then start adding in the rest of your code once successful.

Modify table and column name(s) to suit.

N.B.: This is a basic insertion method. I suggest you use prepared statements instead.

Sidenote: The password column needs to be long enough to accomodate the hash VARCHAR(255). Consult "Footnotes".

INSERT file

<?php
$DB_HOST = 'xxx';
$DB_USER = 'xxx';
$DB_PASS = 'xxx';
$DB_NAME = 'xxx';

$conn = new mysqli($DB_HOST, $DB_USER, $DB_PASS, $DB_NAME);
if($conn->connect_errno > 0) {
die('Connection failed [' . $conn->connect_error . ']');
}

$password = "rasmuslerdorf";
$first_name = "john";
$password = password_hash($password, PASSWORD_DEFAULT);

$sql = "INSERT INTO users (`name`, `password`) VALUES ('" .$first_name ."', '" .$password ."')";

    $query = mysqli_query($conn, $sql);
    if($query)

{
    echo "Success!";
}

else{
    // echo "Error";
    die('There was an error running the query [' . $conn->error . ']');
}

LOGIN file

<?php
// session_start();

$DB_HOST = 'xxx';
$DB_USER = 'xxx';
$DB_PASS = 'xxx';
$DB_NAME = 'xxx';

$conn = new mysqli($DB_HOST, $DB_USER, $DB_PASS, $DB_NAME);
if($conn->connect_errno > 0) {
  die('Connection failed [' . $conn->connect_error . ']');
}

$pwd = "rasmuslerdorf";
$first_name = "john";

//$sql = "SELECT * FROM users WHERE id = 1";

        $sql = "SELECT * FROM users WHERE name='$first_name'";
        $result = $conn->query($sql);
        if ($result->num_rows === 1) {
            $row = $result->fetch_array(MYSQLI_ASSOC);
            if (password_verify($pwd, $row['password'])) {

                //Password matches, so create the session
                // $_SESSION['user'] = $row['user_id'];
                // header("Location: http://www.example.com/logged_in.php");

                echo "Match";

            }else{
                echo  "The username or password do not match";
            }

}

 mysqli_close($conn);

Footnotes:

The password column should be long enough to hold the hash. 72 long is what the hash produces in character length, yet the manual suggests 255.

Reference:

"Use the bcrypt algorithm (default as of PHP 5.5.0). Note that this constant is designed to change over time as new and stronger algorithms are added to PHP. For that reason, the length of the result from using this identifier can change over time. Therefore, it is recommended to store the result in a database column that can expand beyond 60 characters (255 characters would be a good choice)."

RiggsFolly
  • 93,638
  • 21
  • 103
  • 149
Funk Forty Niner
  • 74,450
  • 15
  • 68
  • 141
  • 3
    You were right to have me check the database field length. It wasn't at 255, that's what was breaking it. Thank you very much for helping me figure this out. I'm also looking into prepared statements, I'm so used to preventing SQL injection manually that I never heard of them until recently, from what I've seen so far they're pretty straight forward. Thanks again for the help. –  Oct 24 '14 at 11:55
  • 1
    @Robert You're very much welcome Robert, I'm glad to know that it's been resolved. *Cheers* :) – Funk Forty Niner Oct 24 '14 at 12:04
  • 1
    Why aren't you using prepared statements? There's always a risk that your code will just get copied into a project and the variables replaced by `$_POST['user']`, etc. – Ja͢ck Dec 10 '14 at 12:22
  • 2
    @Ja͢ck I included a link to it. I believe I worked long enough with the OP to provide a solution to the issue at hand. I can't be expected to do up a complete script for every single question I answer that is related to MySQL. It's up to the others to look into the link that contains examples. If that were the case, I would be forced to go into every answer I put in that doesn't contain a complete rewrite. – Funk Forty Niner Dec 10 '14 at 12:31
  • 1
    You're making it seem that using prepared statements takes a big effort while in reality it's just two additional lines of code. – Ja͢ck Dec 10 '14 at 12:33
  • @user5348fh8y5 I would only be defacing the question/answer, so it's best to "leave well enough alone" ;-) Have a look at one of ircmaxell's (great) answers using PDO and a prepared statement here http://stackoverflow.com/a/29778421/1415724 ;-) – Funk Forty Niner Nov 03 '16 at 14:38
0

Friends as we are using Unique Username for Login, So we have to Fetch Password/Data from Database using Username Only.

Example:

<?php
    $connect = mysqli_connect($localhost, $username, $pwd, $database) or die("Opps some thing went wrong");

    if (isset($_POST['submit'])) {
      extract($_POST);

     // Get Old Password from Database which is having unique userName
     $sqlQuery = mysqli_query($connect, "select * from loginTable where User='$username'");
     $res = mysqli_fetch_array($sqlQuery);
     $current_password = $res['userPassword'];

     if (password_verify($enteredPassword, $current_password)) { 
        /* If Password is valid!! */
        $_SESSION['id'] = $res['id'];
        header("location: home.php");
     }
     else { 
        /* If Invalid password Entered */
        $alt = "Login Failed! Wrong user ID or Password";
        header("location: index.php?m=$alt");
     }
  }
?>

It is Working for me... I am fetching Password from database and comparing with entered Password Using PHP API i.e. password_verify($enteredPassword, $current_password)

Irshad Khan
  • 5,670
  • 2
  • 44
  • 39