PHP Login page and MySql

Updated broj1 3 Tallied Votes 777 Views Share

I have prepared an example of login page which displays a form with inputs for username and password and compares submited values with records in database. If match is found the user is redirected to another page, if not error message is displayed. This example is heavy commented. Hope it helps people who need this kind of script.

Please suggest improvements.

<?php
/*
Login script example
- displays a form for entering username and password
- checks wheter username and password exist in database and match
- if no match is found, clears the form and displays an error message
- if exactly one match is found, redirects user to another page

Tip: make page look nicer with some CSS

For this login example you will need working database (mySql used here), and
some test data as per instructions below (or you can use phpmyadmin or similar app)

Test data (2 users):

username 1: misterx
password 1: secretpassword1
hashed password1: (d5f835dbe946b420e1dacde0558078b4eee36745)

username 2: mistery
password 2: secretpassword2
hashed password2: (fd021e83bf64b46a2a7b707441dd167bc43749d4)

Prepare database 'mydatabase' with table 'user' and some test data

1. Use this or similar query to create database 'mydatabase'
CREATE DATABASE `mydatabase` ;

2.create DB user named 'testdbuser' with password 'verysecretdbpassword' and
 granthim privileges
CREATE USER 'testdbuser'@'%' IDENTIFIED BY 'verysecretdbpassword';
GRANT ALL PRIVILEGES ON * . * TO 'testdbuser'@'%'
IDENTIFIED BY 'verysecretdbpassword'
WITH GRANT OPTION MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0
    MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0 ;

3. Use this or similar query to create table 'users' in database 'mydatabase'
CREATE TABLE `mydatabase`.`users` (
`id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'ID (primary key)',
`username` VARCHAR( 24 ) NOT NULL COMMENT 'Username (max 24 chars)',
`hpassword` CHAR( 40 ) NOT NULL COMMENT 'sha1 hashed password'
) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT = 'Users table';

4. Use this query to insert above test data into the table 'users'
INSERT INTO `users` (`id`, `username`, `hpassword`) VALUES (NULL , 'misterx', '298e6df75f76926af93925e7a34e060ea523a363');
INSERT INTO `users` (`id`, `username`, `hpassword`) VALUES (NULL , 'mistery', '05b68c5b67e2c7a95cc86e4ee26778e5d9c77c6c');
*/

// start session
session_start();

// set session variable that identifies valid user to 0 until user submits
// valid username and passwordusername
$_SESSION['valid_user'] = 0;

// a variable that will hold error message if needed
$msg = '';

// check wheter user has submitted a username and/or password
if(isset($_POST['username']) or isset($_POST['password'])) {

    // if both username and password are submitted and not empty
    if(isset($_POST['username']) and !empty($_POST['username']) and
       isset($_POST['password']) and !empty($_POST['password'])) {

        // asign posted values to variables and trim possible spacess before and
        // after the strings
        $username = trim($_POST['username']);
        $password = trim($_POST['password']);

        // passwords stored in the users database are hashed with sha1 therefore
        // submited password has also be hashed so values can be compared
        $hpassword = sha1($password);

        // prepare database connection
        $conn = mysqli_connect('localhost', 'testdbuser', 'verysecretdbpassword', 'mydatabase')
            or die ('ERROR: Can not connect to the database!');

        // prepare query to select a user with submitted username and hashed
        // submitted password (to check for the match)
        $query  = "SELECT username, hpassword FROM users ";
        $query .= "WHERE username='$username' AND hpassword='$hpassword'";

        // get the result of the query
        $res = mysqli_query($conn, $query);

        // if mysqli_query was successful and if one row was returned from query
        // we have a match, the username and password are OK
        // (if no rows returned username and password did not match, if more than
        // 1 row returned we have entered one user more times which is incorrect
        if($res and mysqli_num_rows($res) == 1) {

            // set session variable that identifies valid user to 1
            $_SESSION['valid_user'] = 1;

            // redirect user to login_success.php page
            header("location:login_success.php");

            //just in case anything goes wrong from here end the script
            die();
        }

        // if no rows are returned username and password did not match
        // (or if more than 1 row returned we have entered one user many times
        // which is incorrect)
        else {

            // again set session variable that identifies valid user to 0
            $_SESSION['valid_user'] = 0;

            // prepare error message
            $msg = 'Please enter correct username and password!';
        }
    }

    // if only username or only password was submitted
    else {

        // again set session variable that identifies valid user to 0
        $_SESSION['valid_user'] = 0;

        // prepare error message
        $msg = 'Please enter correct username and password!';
    }
}
?>
<!DOCTYPE html
 PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3c.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">

<head>
<title>Login</title>
</head>

<body>

<!-- Form will be submitted to itself -->
<form action="#" method="post">

<p>Please login</p>

<div class="login"><input name="username" type="text" id="username" /></div>

<div class="login"><input name="password" type="password" id="password" /></div>

<div class="login"><input type="submit" name="submit" value="Login"></div>

<!-- Possible error messages will be displayed here -->
<div class="error-message"><p><?php echo $msg ?></p></div>

</form>

</body>

</html>
broj1 356 Humble servant Featured Poster

Sorry, this was intended to go under tutorials. Might have pushed the wrong button again.

pritaeas 2,194 ¯\_(ツ)_/¯ Moderator Featured Poster

Notify a moderator to make it a tutorial, you cannot do this yourself. You can use 'Flag bad post' to get attention from one.

Nick Evan 4,005 Industrious Poster Team Colleague Featured Poster

This is not so much a tutorial as it is a snippet. So changed to snippet.

cereal 1,524 Nearly a Senior Poster Featured Poster

You can improve it adding a salt to your hashing password. Create salt.php:

<?php
define('SALT','secret string',true);
?>

Then create a directory where you save salt.php and where you also create an .htaccess file:

order allow,deny
deny from all

And then include it in your main script:

include($_SERVER['DOCUMENT_ROOT'].'/safe_path/salt.php');
$hpassword = sha1(SALT.$password);

This way the defined SALT is not clearly readable in the main script and it can't be accessed directly. Bye :)

broj1 356 Humble servant Featured Poster

Hi cereal

I am looking at sha1() function definition on http://php.net/manual/en/function.sha1.php which says:

string sha1(string $str [, bool $raw_output = false ])

Is it possible to use sha1() with salt?

MagicMedia 10 Junior Poster

You're begging for a SQL injection.

cereal 1,524 Nearly a Senior Poster Featured Poster

Hi cereal

I am looking at sha1() function definition on http://php.net/manual/en/function.sha1.php which says:

string sha1(string $str [, bool $raw_output = false ])

Is it possible to use sha1() with salt?

Yes is possibile, between SALT and $password there is a period, not a comma, if you write sha1(SALT,$password) then you will get an error, but with a period you extend the password: "secret_stringyour_password"

Member Avatar for diafol
diafol

like MagicMedia says sqlinjection alive and well. Clean your input.

Stefano Mtangoo 455 Senior Poster

Also instead of mysqli_procedure you can use prepared statement.
preg_match is a life saver when it comes to validation!

eXpertPHP 0 Newbie Poster

Well, this PHP code from first post have too many bugs.. sorry. I apreciate the "free" work, but at least to be good ;)

$query .= "WHERE username='$username' AND hpassword='$hpassword'";

Bug: if the charset on mysql connection/table/row is set to _ci (case insensitive) this query is wrong!
Fix: extract the password value from database into php variable, and safe compare (binary, or byte-to-byte)

if ($_POST['password'] === $row['hpassword'])// true evaluation

Bug: session login must have identifier, 1 is provided

$_SESSION['valid_user'] = 1;

you must NOT register a session if the credentials is are NOT valid, because aditional php hosting resource is use to store values

$_SESSION['valid_user'] = 0;

Fix: in case when credentials are valid, extract from database and store the username value into session variable to make it unique:

$_SESSION['valid_user'] = $row['username'];

Bug: the input form HTML must provide additional secure options, to let the user choose storage client side data procedures (username and/or password). Do NOT use browser default values when security and privacy is involve.

broj1 356 Humble servant Featured Poster

Thanks to everyone for suggested improvements (and sorry for late reply since I was away). I must agree with all of them. The script was firstly prepared as to show concept of login to answer a thread and security part was neglected which I admit was wrong. It is too important a subject so it should be included. The following is a version with some improvements and notices.

Main changes:
1. username input cleaned and note added to use regular expression (which depends on username requirements); password not cleaned since it gets hashed
2. queries to create database, table and data removed since they depend on database used
3. table that holds user data renamed to tableofusers (just another little security good practice not to use comon names for tables and fields)
4. session variable not assigned until login is OK (to save hosting resources)
5. additional level of validation added by comparing password hashes in the script not just within the database query
6. username stored in session instead of login status (depends realy on what you need)
7. suggestions for further improvements added in comments
8. typos corrected and some other minor improvements made

There are also other ways of doing the login script of course. Please use this one as a concept and adapt it to your needs, requirements and environment. Also if you use some depreciated features (such as magic quotes) take them into account.

<?php
/*
Login script example version 2 (added improvements from other posters, thnx to all)
- displays a form for entering username and password
- cleans an input
- checks wheter username and password exist in database and match
- if no match is found, clears the form and displays an error message
- if exactly one match is found, redirects user to another page

For this login example you will need working database (mySql used here), and
some test data as shown below.

Test data for 2 users in database 'mydatabase', table 'tableofusers':

username 1: misterx
password 1: secretpassword1
hashed password1: d5f835dbe946b420e1dacde0558078b4eee36745

username 2: mistery
password 2: secretpassword2
hashed password2: fd021e83bf64b46a2a7b707441dd167bc43749d4
*/

// start session
session_start();

// unset any session data until user submits valid username and password
unset($_SESSION);

// a variable that will hold error message if needed
$msg = '';

// check wheter user has submitted a username and/or password
if(isset($_POST['username']) or isset($_POST['password'])) {

    // if both username and password are submitted and not empty
    if(isset($_POST['username']) and !empty($_POST['username']) and
       isset($_POST['password']) and !empty($_POST['password'])) {

        // prepare database connection
        $conn = mysqli_connect('localhost', 'testdbuser', 'verysecretdbpassword', 'mydatabase')
            or die ('ERROR: Can not connect to the database!');

        // asign posted values to variables but first trim possible spacess
        // (before and after the strings) and clean your input of unwanted chars
        // mysqli_real_escape_string() used here to escape unwanted input chars
        // but do use regular expressions here and remove characters that are
        // not allowed in your username - password gets hashed so it is OK)
        $username = mysqli_real_escape_string($conn, trim($_POST['username']));
        $password = trim($_POST['password']);

        // passwords stored in the users database are hashed with sha1 therefore
        // submited password has also be hashed so values can be compared
        // salt can also be used as sugested by cereal a few posts back
        $hpassword = sha1($password);

        // prepare query to select a user with submitted username and hashed
        // submitted password (to check for the match)
        // note: prepared statement can be used here for more security
        $query  = "SELECT username, hpassword FROM tableofusers ";
        $query .= "WHERE username='$username' AND hpassword='$hpassword'";

        // get the result of the query
        $res = mysqli_query($conn, $query);

        // get a row for comparison
        $row = mysqli_fetch_row($res);

        // if mysqli_query was successful and if one row was returned from query
        // and password hashes match exactly, the username and password are OK
        // (if no rows returned username and password did not match, if more than
        // 1 row returned we have entered one user more times which is incorrect
        if($res and mysqli_num_rows($res) == 1 and $row[1] === $hpassword) {

            // set session variable that identifies valid user to 1 or true
            // or like in this example store username (depends on what you need)
            $_SESSION['username'] = $username;

            // redirect user to login_success.php page
            header("location:login_success.php");

            //just in case anything goes wrong from here end the script
            die();
        }

        // if no rows are returned username and password did not match
        // (or if more than 1 row returned we have entered one user many times
        // which is incorrect)
        else {

            // unset any session data until user submits valid username and password
            // this is not quite necessary, just to be sure session is cleared
            unset($_SESSION);

            // prepare error message
            $msg = 'Please enter correct username and password!';
        }
    }

    // if only username or only password was submitted
    else {

        // unset any session data until user submits valid username and password
        // this is not quite necessary, just to be sure session is cleared
        unset($_SESSION);

        // prepare error message
        $msg = 'Please enter correct username and password!';
    }
}
?>
<!DOCTYPE html
 PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3c.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">

<head>
<title>Login</title>
</head>

<body>

<!-- Form will be submitted to itself -->
<form action="#" method="post">

<p>Please login</p>

<div class="login"><input name="username" type="text" id="username" /></div>

<div class="login"><input name="password" type="password" id="password" /></div>

<div class="login"><input type="submit" name="submit" value="Login"></div>

<!-- Possible error messages will be displayed here -->
<div class="error-message"><p><?php echo $msg ?></p></div>

</form>

</body>

</html>
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.