简体   繁体   中英

How to debug password verification problems in PHP login form?

Everything is OK with registration form but I can't figure out how to complete login form. I searched a lot but without no luck, I'm new to PHP. Please give me tips and solutions.

if (isset($_POST['username']) && isset($_POST['password']))
{
  $username = $_POST['username'];
  $_SESSION['LoggedIn'] = $username; //store username in session
  $password = $_POST['password'];

  // To protect MySQL injection (more detail about MySQL injection)
  $username = stripslashes($username);
  $password = stripslashes($password);
  // $username = mysql_real_escape_string($username);
  // $password = mysql_real_escape_string($password);
  $hash_password = password_hash($password , PASSWORD_DEFAULT);
  $verify_password = password_verify($_POST['password'] , $hash_password);


  // check login errors
  $check_login_errors = LoginErrors($username , $password);
  if ($check_login_errors)
     {
       //search database
       $query = "SELECT * FROM users WHERE Username='$username' AND Password='$hash_password'";
       $query_result = mysqli_query($connection , $query);

       //check database for username
      if (mysqli_fetch_array($query_result,MYSQLI_NUM > 0))
       {
        //verify password and username
        if($verify_password == $username)
         {
          echo "logged in";
          $_SESSION['LoggedIn'];
          RedirectTo("profile.php");
         }
        else
         {
          echo "wrong password or username";
         }
       }
      else
      {
        echo "error with checking username in database";
      }
    }
  }

 ?> 

Everything I try it give me the last error:

error with checking username in database

Note I added session_start() at the top of my PHP code too.

I believe you are not using password_verify correctly here - the hashed password is already in the database ( presumably otherwise what is the point of the hashing? ) so you need to compare that value with the posted password. I modified the code to use prepared statements as the original was potentially still vulnerable to SQL injection.

Also, the use of stripslashes before hashing/verifying the password would have corrupted the password in some circumstances. For example, this would have prevented passwords containing backslashes ( \\ ) from working, and that is a perfectly legal character to have in a password.

if( isset( $_POST['username'], $_POST['password'] ) ){

    $username = $_POST['username'];
    $password = $_POST['password'];

    $stmt=$connection->prepare(
        'select `username`,`password` from `users` where `username`=?'
    );
    if( $stmt ){

        $stmt->bind_param( 's', $username );
        $result = $stmt->execute();

        if( $result ){
            $stmt->store_result();
            $stmt->bind_result( $uname,$upwd );
            $stmt->fetch();

            $stmt->free_result();
            $stmt->close();

            /* compare POSTed password and HASH from db */
            $verify = password_verify( $password, $upwd );

            if( $verify ){
                $_SESSION['LoggedIn'] = $username;
                header('Location: profile.php' );
            } else {
                /* more bad foo */
                echo "wrong password or username";
            }
        } else {
            echo "bad foo: error with checking username in database";
        }
    } else {
        echo "Failed to prepare sql query";
    }
}

--

From the PHP manual

The prepared statement execution consists of two stages: prepare and execute. At the prepare stage a statement template is sent to the database server. The server performs a syntax check and initializes server internal resources for later use.

Prepare is followed by execute . During execute the client binds parameter values and sends them to the server. The server creates a statement from the statement template and the bound values to execute it using the previously created internal resources.

So, to break it down:

$sql='select `username`,`password` from `users` where `username`=?';

The sql statement fetches two columns, though really for this only the password is required. Note the use of the ? - that is the placeholder to which we will bind a variable - in this case the $username . If the prepare method fails it will return false so we can use that as a logic test to proceed or bailout.

$stmt->prepare( $sql );

Prepares the SQL query, and returns a statement handle to be used for further operations on the statement. The query must consist of a single SQL statement.
[ Read more about $stmt->prepare() ]

$stmt->bind_param( 's', $username );

The statement must have been prepared successfully at this point so we bind the $username variable to the placeholder. As the user name is a string we use s as the type. There are four types possible, and they are as follows:

i   corresponding variable has type integer
d   corresponding variable has type double
s   corresponding variable has type string
b   corresponding variable is a blob and will be sent in packets

[ Read more about $stmt->bind_param() ]

$result = $stmt->execute();

Now that the statement is prepared and all variables are bound to their placeholders, commit the query and use the response for further logic tests as this returns TRUE on success or FALSE on failure.
[ Read more about $stmt->execute() ]

$stmt->store_result();

The enables us to work with the returned recordset
[ Read more about $stmt->store_result() ]

$stmt->bind_result( $uname,$upwd );

Binds variables to a prepared statement for result storage. As the query fetched two columns we bind two variables - they do not exist at this stage but will become available when we fetch the actual recordset.
[ Read more about $stmt->bind_result() ]

$stmt->fetch();

Fetch results from a prepared statement into the bound variables. This makes $uname & $upwd available as variables to use however we wish ( within reason )
[ Read more about $stmt->fetch() ]

You are making a small mistake here in your below if condition:-

if (mysqli_fetch_array($query_result,MYSQLI_NUM > 0))

Use this instead:

if (mysqli_fetch_array($query_result,MYSQLI_NUM) > 0)

Syntax:- mysqli_fetch_array(result,resulttype);

Just for sake of sanity, a usable in the real life version of the code from the accepted answer. Surely you aren't going to write a screenful of code to run one single alone query, are you?

if( isset( $_POST['username'], $_POST['password'] ) ){
    $sql = 'select `username`,`password` from `users` where `username`=?';
    $stmt = $connection->prepare($sql);
    $stmt->bind_param( 's', $_POST['username'] );
    $stmt->execute();
    $stmt->bind_result( $uname,$upwd );
    if ($stmt->fetch() && password_verify( $_POST['password'], $upwd );
        $_SESSION['LoggedIn'] = $username;
        header('Location: profile.php' );
        exit;
    }
    echo "wrong password or username";
}

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