Search code examples
phpmysqlprepared-statement

Login system with prepared statements mysqli


Im struggling to convert my login system to prepared statements. I have managed to convert my entire Crud system but just confused with this last part. Any help would be much appreciated.

This was the original login without prepared statements:

if(isset($_POST['submit'])) {
    $user = mysqli_real_escape_string($con, $_POST['username']);
    $password = mysqli_real_escape_string($con, $_POST['password']);
 
    if($user == "" || $password == "") {
        echo "Either username or password field is empty.";
        echo "<br/>";
        echo "<a href='login.php'>Go back</a>";
    } else {
        $result = mysqli_query($con, "SELECT * FROM login WHERE username='$user' AND password=md5('$password')")
        or die("Could not execute the select query.");
        
        $row = mysqli_fetch_assoc($result);
        
        if(is_array($row) && !empty($row)) {
            $validuser = $row['username'];
            $_SESSION['valid'] = $validuser;
            $_SESSION['name'] = $row['name'];
            $_SESSION['id'] = $row['id'];
        } else {
            echo "Invalid username or password.";
            echo "<br/>";
            echo "<a href='login.php'>Go back</a>";
        }
 
        if(isset($_SESSION['valid'])) {
            header('Location: index.php');            
        }
    }
} else {
?>
    <div class="row">
    <div class="col-md-6 col-md-offset-3">
        <div class="box">
            <h3>Login</h3> 
            <form name="form1" action="" method="POST" enctype="multipart/form-data">
                <input type="text"  name="username" id="username" placeholder="Username" class="form-control"><br>
                <input type="password"  name="password" id="password" placeholder="Password" class="form-control"><br>
                <button type="submit" name="submit" class="btn btn-success button">Login</button>
            </form>
        </div>
    </div>
<?php
}
?>
</body>
</html>

And then my index.php page

<?php
    if(isset($_SESSION['valid'])) {            
        include("connect.php");                    
        $result = mysqli_query($con, "SELECT * FROM login");
    ?>                
        Welcome <?php echo $_SESSION['name'] ?> ! <a href='logout.php'>Logout</a><br/>
        <br/>
        <a href='view.php'><?php echo ("<div class='alert alert-success'>Click here to View and Add Products!</div>")?></a>
        <a href='viewuser.php'><?php echo ("<div class='alert alert-success'>Click here to Add Users!</div>")?></a>
        <br/><br/>
    <?php    
    } else {
        echo ("<div class='alert alert-danger'>You must be logged in to access CMS site.</div>"); 
        echo "<a href='login.php' class='btn btn-primary'>Login</a>";
    }
    ?>

This is what I have done so far:

if(isset($_POST['submit'])) {
    $user = $_POST['username'];
    $password = md5(['password']);
 
            $sql = $con->prepare("SELECT * FROM login WHERE username = ? AND password = ?");
            $sql->bind_param("ss", $user, $password);
            $sql->execute();
            $result = $sql->get_result();
            while($row = $result->fetch_assoc()) {
            

        
        $row = mysqli_fetch_assoc($result);  
        
        if(is_array($row) && !empty($row)) {
            $validuser = $row['username'];
            $_SESSION['valid'] = $validuser;
            $_SESSION['name'] = $row['name'];
            $_SESSION['id'] = $row['id'];
        } else {
            echo "Invalid username or password.";
            echo "<br/>";
            echo "<a href='login.php'>Go back</a>";
        }
 
        if(isset($_SESSION['valid'])) {
            header('Location: index.php');            
        }
    }
}
?>
    <div class="row">
    <div class="col-md-6 col-md-offset-3">
        <div class="box">
            <h3>Login</h3> 
            <form name="form1" action="" method="POST" enctype="multipart/form-data">
                <input type="text"  name="username" id="username" placeholder="Username" class="form-control"><br>
                <input type="password"  name="password" id="password" placeholder="Password" class="form-control"><br>
                <button type="submit" name="submit" class="btn btn-success button">Login</button>
            </form>
        </div>
    </div>
</body>
</html>

Its on this part that I am getting so confused as to how to implement it in prepared statements with the index.php No matter what I try from searching the internet, I just cant seems to get this part figured out:

$row = mysqli_fetch_assoc($result);
        
        if(is_array($row) && !empty($row)) {
            $validuser = $row['username'];
            $_SESSION['valid'] = $validuser;
            $_SESSION['name'] = $row['name'];
            $_SESSION['id'] = $row['id'];
        } else {
            echo "Invalid username or password.";
            echo "<br/>";
            echo "<a href='login.php'>Go back</a>";
        }
 
        if(isset($_SESSION['valid'])) {
            header('Location: index.php');            
        }
    }
}

Solution

  • As the comments already point out, one should use an appropriate function like password_hash(), and store the hashes in a field of type varchar(255):

    // Hash a new password for storing in the database.
    // The function automatically generates a cryptographically safe salt.
    $hashToStoreInDb = password_hash($password, PASSWORD_DEFAULT);
    

    In the login form, we cannot verify the password directly with SQL, nor can we search for it, because the stored hashes are salted and the calculation of a single hash takes a lot of time. Instead we...

    1. have to read the password-hash from the database, searching by the user id
    2. and afterwards can check the login password against the found hash with the password_verify() function.

    Below you can find a code example for mysqli, showing how to do the password verification with a mysqli connection. The code has no error checking to make it readable:

    /**
     * mysqli example for a login with a stored password-hash
     */
    $mysqli = new mysqli($dbHost, $dbUser, $dbPassword, $dbName);
    $mysqli->set_charset('utf8');
    
    // Find the stored password hash in the db, searching by username or email
    $sql = 'SELECT password FROM users WHERE username = ?';
    $stmt = $mysqli->prepare($sql);
    $stmt->bind_param('s', $_POST['username']); // it is safe to pass the user input unescaped
    $stmt->execute();
    
    // If this user exists, fetch the password-hash and check it
    $isPasswordCorrect = false;
    $stmt->bind_result($hashFromDb);
    if ($stmt->fetch() === true)
    {
      // Check whether the entered password matches the stored hash.
      // The salt and the cost factor will be extracted from $hashFromDb.
      $isPasswordCorrect = password_verify($_POST['password'], $hashFromDb);
    }