How to validate username and password in php

How to validate username and password in php?

  1. Receive $_POST[‘username’]
  2. Receive $_POST[‘password’]
  3. Validate or confirm that $_POST[‘username’] holds expected data
  4. Validate or confirm that $_POST[‘password’] holds expected data
  5. Retrieve user’s salt and password hash from the database
  6. Hash user’s entered password with the retrieved database salt to get same password hash as stored in the database
  7. Compare the result of your hash with the password hash retrieved from the database.
  8. If the result of your comparison is equal; allow user to login, else disallow login.

Now let me show you how to achieve all of this. So consider the HTML Login Form form below:

<html lang="en">
    <head>
        <script>
            function myRevealPasswordFunction() {
                var x = document.getElementById("passwd");
                if (x.type === "password") {
                  x.type = "text";
                } else {
                  x.type = "password";
                }
            }
        </script>
        <!--===============================================================================================-->
    </head>
    <body>
        <?php
        $loginFormToken = Security::Generate_Random_Md5_Token_Against_Form_Spoofing();
        //store generated token in a session variable for comparism in the form processing page
        $_SESSION['loginFormToken'] = $loginFormToken;
        ?>
        <form action="Process_Login.php" method="post" class="login100-form validate-form p-l-55 p-r-55 p-t-178">
            <input type="hidden" name="loginToken" value="<?= $loginFormToken ?>" />
            <span class="login100-form-title">
                Sign In
            </span>
            <div class="wrap-input100 validate-input m-b-16" data-validate="Please enter username">
                <input class="input100" type="text" name="username" placeholder="Username">
                <span class="focus-input100"></span>
            </div>

            <div class="wrap-input100 validate-input" data-validate = "Please enter password">
                <input id="passwd" class="input100" type="password" name="password" placeholder="Password">
                <span class="focus-input100"></span>
            </div>
            <input type="checkbox" onclick="myRevealPasswordFunction()"><span href="#" class="txt2">Show Password</span>

            <div class="container-login100-form-btn">
                <button class="login100-form-btn">
                    Sign in
                </button>
            </div>
        </form>
    </body>
</html>

So after the form is posted to the page: Process_Login.php, we receive the username and password posted and make sure we validate the form inputs to be sure it is the data we are expecting.

$username = validate_inputs::Validate_Username_During_Login($_POST['username'], 'http://' . $_SERVER['HTTP_HOST'] . '/Login/index.php', 'Your username or password is incorrect');
$password = Validate_Password_During_Login($_POST['password'], 'http://' . $_SERVER['HTTP_HOST'] . '/Login/index.php', 'Your username or password is incorrect');

Notice the third parameter in the function Validate_Username_During_Login and Validate_Password_During_Login. This passes what message is to be printed back to user in case a user supplies wrong username in the case of Validate_Username_During_Login or wrong password in the case of Validate_Password_During_Login. But notice that both functions’ message says: ‘Your username or password is incorrect’. This is so because in case an attacker is trying to break into our application, then such an attacker should not be given the exact message. For instance say username is not correct but password is correct, if you display the error message: your username is not correct – suggesting that the password is correct; now the attacker knows that such a password do exist in our database.

The second parameter of both function which is a url is used to redirect a user appropriately should login detail fail. Please see both functions below to understand better.

Validate_Username_During_Login and Validate_Password_During_Login is a public static method of the validate_inputs class which i have written and is shown below. These two functions makes sure that we are dealing with expected data. In writing secure code we treat all data as suspect unless proven otherwise. The only way to prove that we are dealing with the right data is by validating inputs – which is what we seek to achieve with these two functions. See the two functions in the code below.

<?php
class Validate_inputs {
     static public function Validate_Username_During_Login($string, $URL, $errno) {
        $string = trim($string);
        //must not contain any character other than Uppercase and lowercase alphabets including . or _
        if (preg_match('/[^A-Za-z._]/', $string) === 1) {
            if ($URL === 'http://' . $_SERVER['HTTP_HOST'] . '/index.php') {
                session_unset(); //unset session variables
                session_destroy(); //destroy session completely
            }
            //$errno = 1;
            if (headers_sent()) {//if something went wrong with redirect, display link instead
                echo "Please click on this link : <a href=$URL?msp=$errno>Link</a>";
                return FALSE;
            } else {
                header("location: $URL?msp=$errno");
                return FALSE;
            }
            return FALSE;
        }
        //contain at least 2 Uppercase alphabet
        if (preg_match_all('/[A-Z]/', $string) < 2) {
            if ($URL === 'http://' . $_SERVER['HTTP_HOST'] . '/index.php') {
                session_unset(); //unset session variables
                session_destroy(); //destroy session completely
            }
            //redirect back to where user entered username
            //$errno = 1;
            if (headers_sent()) {//if something went wrong with redirect, display link instead
                echo "Please click on this link : <a href=$URL?msp=$errno>Link</a>";
                return FALSE;
            } else {
                header("location: $URL?msp=$errno");
                return FALSE;
            }
            return FALSE;
        }
        //contain at least 2 lowercase alphabet
        if (preg_match_all('/[a-z]/', $string) < 2) {
            if ($URL === 'http://' . $_SERVER['HTTP_HOST'] . '/index.php') {
                session_unset(); //unset session variables
                session_destroy(); //destroy session completely
            }
            //contain at least 2 lowwercase alphabet
            //$errno = 1;
            if (headers_sent()) {//if something went wrong with redirect, display link instead
                echo "Please click on this link : <a href=$URL?msp=$errno>Link</a>";
                return FALSE;
            } else {
                header("location: $URL?msp=$errno");
                return FALSE;
            }
            return FALSE;
        }
        //Contain either a dot or an underscore
        if (preg_match('/[.|_]/', $string) === 0) {
            if ($URL === 'http://' . $_SERVER['HTTP_HOST'] . '/index.php') {
                session_unset(); //unset session variables
                session_destroy(); //destroy session completely
            }
            //$errno = 1;
            if (headers_sent()) {//if something went wrong with redirect, display link instead
                echo "Please click on this link : <a href=$URL?msp=$errno>Link</a>";
                return FALSE;
            } else {
                header("location: $URL?msp=$errno");
                return FALSE;
            }
            return FALSE;
        }
        //contain minimum of 8 chars
        if (strlen($string) < 8) {
            if ($URL === 'http://' . $_SERVER['HTTP_HOST'] . '/index.php') {
                session_unset(); //unset session variables
                session_destroy(); //destroy session completely
            }
            //$errno = 1;
            if (headers_sent()) {//if something went wrong with redirect, display link instead
                echo "Please click on this link : <a href=$URL?msp=$errno>Link</a>";
                return FALSE;
            } else {
                header("location: $URL?msp=$errno");
                return FALSE;
            }
            return FALSE;
        }
        //contain maximum of 12 chars
        if (strlen($string) > 12) {
            if ($URL === 'http://' . $_SERVER['HTTP_HOST'] . '/index.php') {
                session_unset(); //unset session variables
                session_destroy(); //destroy session completely
            }
            //$errno = 1;
            if (headers_sent()) {//if something went wrong with redirect, display link instead
                echo "Please click on this link : <a href=$URL?msp=$errno>Link</a>";
                return FALSE;
            } else {
                header("location: $URL?msp=$errno");
                return FALSE;
            }
            return FALSE;
        }
        return $string;
    }

    static public function Validate_Password_During_Login($string, $URL, $errno) {
        //contain at least 2 Uppercase alphabet
        if (preg_match_all('/[A-Z]/', $string) < 2) {
            if ($URL === 'http://' . $_SERVER['HTTP_HOST'] . '/index.php') {
                session_unset(); //unset session variables
                session_destroy(); //destroy session completely
            }
            //redirect back to where user entered password
            //$errno = 1;
            if (headers_sent()) {//if something went wrong with redirect, display link instead
                echo "Please click on this link : <a href=$URL?msp=$errno>Link</a>";
                return FALSE;
            } else {
                header("location: $URL?msp=$errno");
                return FALSE;
            }
            return FALSE;
        }
        //contain at least 2 lowercase alphabet
        if (preg_match_all('/[a-z]/', $string) < 2) {
            if ($URL === 'http://' . $_SERVER['HTTP_HOST'] . '/index.php') {
                session_unset(); //unset session variables
                session_destroy(); //destroy session completely
            }
            //contain at least 2 lowwercase alphabet
            //$errno = 1;
            if (headers_sent()) {//if something went wrong with redirect, display link instead
                echo "Please click on this link : <a href=$URL?msp=$errno>Link</a>";
                return FALSE;
            } else {
                header("location: $URL?msp=$errno");
                return FALSE;
            }
            return FALSE;
        }
        //contain at least 2 digit
        if (preg_match_all('/[0-9]/', $string) < 2) {
            if ($URL === 'http://' . $_SERVER['HTTP_HOST'] . '/index.php') {
                session_unset(); //unset session variables
                session_destroy(); //destroy session completely
            }
            //$errno = 1;
            if (headers_sent()) {//if something went wrong with redirect, display link instead
                echo "Please click on this link : <a href=$URL?msp=$errno>Link</a>";
                return FALSE;
            } else {
                header("location: $URL?msp=$errno");
                return FALSE;
            }
            return FALSE;
        }
        //contain at least 2 special characters
        if (preg_match_all('/[\W]/', $string) < 2) {
            if ($URL === 'http://' . $_SERVER['HTTP_HOST'] . '/index.php') {
                session_unset(); //unset session variables
                session_destroy(); //destroy session completely
            }
            //$errno = 1;
            if (headers_sent()) {//if something went wrong with redirect, display link instead
                echo "Please click on this link : <a href=$URL?msp=$errno>Link</a>";
                return FALSE;
            } else {
                header("location: $URL?msp=$errno");
                return FALSE;
            }
            return FALSE;
        }
        //contain minimum of 8 chars
        if (strlen($string) < 8) {
            if ($URL === 'http://' . $_SERVER['HTTP_HOST'] . '/index.php') {
                session_unset(); //unset session variables
                session_destroy(); //destroy session completely
            }
            //$Uppercase_alph = TRUE;
            //$errno = 1;
            if (headers_sent()) {//if something went wrong with redirect, display link instead
                echo "Please click on this link : <a href=$URL?msp=$errno>Link</a>";
                return FALSE;
            } else {
                header("location: $URL?msp=$errno");
                return FALSE;
            }
            return FALSE;
        }
        //contain maximum of 12 chars
        if (strlen($string) > 12) {
            if ($URL === 'http://' . $_SERVER['HTTP_HOST'] . '/index.php') {
                session_unset(); //unset session variables
                session_destroy(); //destroy session completely
            }
            //$Uppercase_alph = TRUE;
            //$errno = 1;
            if (headers_sent()) {//if something went wrong with redirect, display link instead
                echo "Please click on this link : <a href=$URL?msp=$errno>Link</a>";
                return FALSE;
            } else {
                header("location: $URL?msp=$errno");
                return FALSE;
            }
            return FALSE;
        }
        return $string;
    }
}
?>

What i am checking for with the username validation is that the username

  • must not contain any character other than Uppercase and lowercase alphabets including . or _
  • contain at least 2 Uppercase alphabet
  • contain at least 2 lowercase alphabet
  • Contain either a dot or an underscore
  • contain minimum of 8 chars
  • contain maximum of 12 chars

Then password must:

  • contain at least 2 Uppercase alphabet
  • contain at least 2 lowercase alphabet
  • contain at least 2 digit
  • contain at least 2 special characters
  • contain minimum of 8 chars
  • contain maximum of 12 chars

But note that each of these rules was first of all enforced during the user’s registration process. So any person that tries to login into our system with incorrect credentials has to be treated as a suspect. The truth is that most of the time it is an automated attack and not a human person who is trying to break into our application.

How to check if username and password matches the database values

Having validated inputs, we can now retrieve the user’s credential from database to compare with what the user supplied.

Retrieve user’s salt and password from database

So password’s are not stored bare in the database because if an attacker should break into your database, then such an attacker can have access to individual accounts using the passwords.

So what happens normally is that a salt (which should be made up of hard to guess characters) is combined with a user’s chosen password and hashed together with some hashing algorithm. This happens during user’s registration; both the salt and the user’s password is stored in the database against the user’s record. Please note that the salt is define by you the programmer.

Now during login, that same salt stored against that user’s record is retrieved along side the password hash, They password that the user supplies through the form post is then hashed the same way it was during registration using the retrieved database salt. The password retrieved from the database is then compared with what has just been hashed. If the supplied password is same as that used during registration, then both hashes should produce same output given the fact that it is the same hash algorithm and salt that is applied to the same password. Make sense right?

So here is how it is done:

First do this in the page that processes user’s registration

//hash the password
$hash = hash('sha256', $password);

Then create salt

//create a 3 character sequence AND concatenate to the current week number and the string 'OlugbaderoGanGan - can be anything you choose'
$salt = Security::createSalt() . (string) date('W') . 'OlugbaderoGanGan';

Just to avoid any confusion, i give you the public static function of the Security class called createSalt() which generates the 3 character sequence here

<?php
//session_start();
Class Security{
        static public function createSalt(){
            $string = md5(uniqid(rand(), true));
            return substr ($string, 0, 3);
        }
}
?>

Now concatenate the salt produced to the hash produced earlier and then hash all over again with the SHA-256 Cryptographic Hash Algorithm

$hash = hash('sha256', $salt . $hash);

And now we have our password hash which is then stored in the database. The salt is also stored in the database.

So now during login as i have said before we take the user’s submitted password using the retrieved salt stored in the database against these user’s record and hash it in the same manner, hoping to get the same output as the password hash stored in the database.

So first retrieve user’s salt and password hash from database

try {
                $sql = "SELECT Password,salt FROM user WHERE Username=:Username";
                $q = $this->conn->prepare($sql);
                $q->bindValue(':Username', $username, PDO::PARAM_STR);
                $q->execute();
                $q->bindColumn(1, $db_password);
                $q->bindColumn(2, $db_salt);
                $q->fetch();
            } catch (PDOException $e) {
                //if anything goes wrong with our query, handle the error here gracefully.
            }

Go ahead and do the hash now with the user’s supplied password using the salt we just retrieved from the database.

$rehashed_passwd = hash('sha256', $db_salt . hash('sha256', $password));

Now compare the retrieved database password hash with the rehashed password.

If ($db_password == $rehashed_passwd ) {
      //Then password is same, allow user to login
}

So if you find both password not to be same, stop user from gaining access to the system; else allow the user to login. SIMPLE.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

You May Also Like