Hello everyone, I hope everything is well with you. So in this article, we'll go through how to build your own secure PHP login system with session time-in and out, similar to the one I've previously developed for the sake of this lesson. A login form on your site or on a website is used to restrict unauthorised access to certain parts of the site. Every site that allows visitors to create accounts needs a secure login and signup form.I'll explain how I built a secure php system and share the code with you later.
##Prerequisites. You should be familiar with PHP (operators,arrays,super global arrays,...). If you don't already have a local web server, I recommend XAMPP or Laragon. Knowledge of Sql basic syntax queries. Knowledge of HTML and CSS.
##What You'll learn. How to create a login system. Session management. Php form validation.
##Setup & Structure of Files. After you've downloaded the code from the github source here. Please remember to leave a star. This is how the file structure should look even if it hasn't been properly organised.
###File Structure |--assets/ |--index.php |--process.php |--login.php |--signup.php |--dbconfig.php |-database.sql |--logout.php
Frontend Design
This project's html template has already been developed and is designed with Bootstrap.
###Step 1 - Creating the database and Setting-Up the tables You will need to access to your MySQL database for this section, either using phpMyAdmin or your favourite database administration software.
- Click on the Database Tab
- Navigate to the create database tab in your database software
- Create a database called
secure
- Navigate to the sql tab paste the sql code from the
database.sql
file.
If you use phpmyadmin, follow these steps:
- Go to: localhost/phpmyadmin
- At the top, select the Databases tab.
*In the text box under Create database, type
secure
. - Select the Sql tab and paste the content of the
database.sql
file The code you paste should look like: ```sql -- phpMyAdmin SQL Dump -- version 5.1.0-- phpmyadmin.net
-- Host: localhost:3306 -- Generation Time: Aug 22, 2021 at 04:58 PM -- Server version: 5.7.33 -- PHP Version: 7.4.19
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; START TRANSACTION; SET time_zone = "+00:00";
/!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT /; /!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS /; /!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION /; /!40101 SET NAMES utf8mb4 /;
--
-- Database: secure
--
-- Table structure for table user
CREATE TABLE user
(
id
int(8) NOT NULL,
fullName
varchar(300) NOT NULL,
email
varchar(300) NOT NULL,
password
varchar(300) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Indexes for table user
ALTER TABLE user
ADD PRIMARY KEY (id
);
--
-- AUTO_INCREMENT for dumped tables
--
-- AUTO_INCREMENT for table user
ALTER TABLE user
MODIFY id
int(8) NOT NULL AUTO_INCREMENT;
COMMIT;
/!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT /; /!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS /; /!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION /;
The SQL statement code above would create a `user` table, which will have the columns id, fullName, password, and email.
###Step 2- Connecting Script to Database
The `dbconfig.php`file contains the following code:
```php
<?php
$err_message = '';
$message = '';
// Change this to your connection info.
define('DBHOST', 'localhost'); //define your website host
define('DBUSER', 'root');//define your database username
define('DBPASS', '');//define your database password leave if no password is set
define('DBNAME', 'secure');// database name
// Try and connect using the info above.
try {
$pdo = new PDO("mysql:host=".DBHOST.";dbname=".DBNAME, DBUSER, DBPASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
// If there is an error with the connection, stop the script and display the error.
catch( PDOException $exception ) {
echo "Connection error :" . $exception->getMessage();
}
// If there is an error with the connection, stop the script and display the error.
catch( PDOException $exception ) {
echo "Connection error :" . $exception->getMessage();
}
The first thing I did was create two variables, $err message and $message, to store the error and success messages respectively.I then created a constant that i would use to connect the script to the database.If you notice that PDO was used to connect the database instead of mysqli, this is because PDO can connect to any database like mogo, but mysqli is solely restricted to mysql databases.Note: You must have the PDO extension enabled in order for the script to function properly.It is necessary to connect to the database. How can we retrieve and keep information about our users without it? As a result, we must ensure that we connect to our database.
###Step 3- Developing a Signup Form
In the Signup.php
file
<?php require_once('dbconfig.php') ?>
<?php include('process.php') ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>Signup-php login System</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Karla">
<link rel="stylesheet" href="assets/css/Registration-Form-with-Photo.css">
<link rel="stylesheet" href="assets/css/styles.css">
</head>
<body style="font-family: Karla, sans-serif;">
<div class="page">
<section class="register-photo">
<div class="form-container">
<div class="image-holder"></div>
<form method="POST">
<h2 class="text-center"><strong>Create</strong> an account.</h2>
<?php if ($err_message) : ?>
<div class="callout text-danger">
<p><?php echo $err_message; ?></p>
</div>
<?php endif; ?>
<?php if ($message) : ?>
<div class="callout text-primary">
<p><?php echo $message; ?></p>
</div>
<?php endif; ?>
<div class="mb-3"><input class="form-control" type="text" name="fullName" placeholder="Full Name" value="<?php if (isset($_POST['fullName'])) echo $_POST['fullName']; ?>" ></div>
<div class="mb-3"><input class="form-control" type="email" name="email" placeholder="Email" value="<?php if (isset($_POST['email'])) echo $_POST['email']; ?>" ></div>
<div class="mb-3"><input class="form-control" type="password" name="password" placeholder="Password" minlength="8" required></div>
<div class="mb-3"><input class="form-control" type="password" name="re_password" placeholder="Password (repeat)" ></div>
<div class="mb-3">
<div class="form-check"><label class="form-check-label"><input class="form-check-input" type="checkbox"required>I agree to the license terms.</label></div>
</div>
<div class="mb-3"><button class="btn btn-primary d-block w-100" type="submit" name="signup" required>Sign Up</button></div><a class="already" href="login.php">You already have an account? Login here.</a>
</form>
</div>
</section>
</div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
</body>
</html>
You begin by including both the dbconfig.php and the process.php files, followed by the html code.When building a form that contains sensitive data that is transmitted to the server, the Post method method="POST"
should be used since it allows us to process the form data using the POST request method and save it as key-value pairs in the body while keeping it hidden from the url, unlike the GET request method.
- The
<?php if ($err_message) : ?>
checks to see if the error message has been set The message will be shown if the $err_message variable is set, and vice versa for the $message variable.<?php if ($message) : ?>
- Input (text/email/password):We must name our form fields in order for the server to identify them. We may define the value of the attribute
name
asfullName
, which we can use to access the post array in our authentication file to receive the data,eg$_POST['fullName']
. - Button(type=submit):When you click the submit button, the information will be submitted to our authentication file for processing.
value="<?php if (isset($_POST['email'])) echo $_POST['email']; ?>
Checks if the array has a value; if an error occurs, it returns the value to the field.
###Step 4- Authenticating users
In the Process.php
the first code encountered is:
<?php
//Create Session
ob_start();
session_start();
//If the Signup button is triggered
if (isset($_POST['signup'])) {
$valid = 1;
/* Validation to check if fullname is inputed */
if (empty($_POST['fullName'])) {
$valid = 0;
$err_message .= " FullName can not be empty<br>";
}
//Filtering fullname if not empty
else {
$fullname = filter_var($_POST['fullName'], FILTER_SANITIZE_STRING);
}
/* Validation to check if email is inputed */
if (empty($_POST['email'])) {
$valid = 0;
$err_message .= 'Email address can not be empty<br>';
}
/* Validation to check if email is valid */ else {
if (filter_var($_POST['email'], FILTER_VALIDATE_EMAIL) === false) {
$valid = 0;
$err_message .= 'Email address must be valid<br>';
}
/* Validation to check if email already exist */ else {
// Prepare our SQL, preparing the SQL statement will prevent SQL injection.
$statement = $pdo->prepare("SELECT * FROM user WHERE email=?");
$statement->execute(array($_POST['email']));
$total = $statement->rowCount();
//If There is a match gives an error message
if ($total) {
$valid = 0;
$err_message .= 'An account is already asociated with the provided email address<br>';
}
}
}
/* Validation to check if password is inputed */
if (empty($_POST['password']) || empty($_POST['re_password'])) {
$valid = 0;
$err_message .= "Password can not be empty<br>";
}
/* Validation to check if passwords match*/
if (!empty($_POST['password']) && !empty($_POST['re_password'])) {
if ($_POST['password'] != $_POST['re_password']) {
$valid = 0;
$err_message .= "Passwords do not match<br>";
}
//If Passwords Matches Generates Hash
else {
//Generating Password hash
$password = filter_var($_POST['password'], FILTER_SANITIZE_STRING);
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
}
}
if ($valid == 1) {
//if There Error Messages are empty Store data in the database
if (empty($err_message)) {
//Saving Data Into Database
$statement = $pdo->prepare("INSERT INTO user (fullName,email,password) VALUES (?,?,?)");
$statement->execute(array($fullname, $_POST['email'], $hashed_password));
$message .= "You have successfully registered proceed to <a href='login.php'>Login</a>.";
} else {
$err_message .= "Problem in registration.Please check your connection and Try Again!";
}
}
}
if (isset($_POST['login'])) { ...
}
First, we create a session, then we check if the sign up button is triggered, and then we create a variable called $valid and set it to 1; this means that for everything in the form to be correct, the valid must be 1. Next, we check if the fullName field is empty, and if it is, we set the $valid variable to 0; if it is not, we filter the submitted data to ensure that no malicious string that can cause sql errors is present. Note the presence of a concatenation operator or dot(.) in front of the = sign in the err_ message field. It is a shortcut for adding to an existing string err message defined in the dbconfig.php
file.The same is done for our email and password fields, and a sql query is produced to determine if the given email address exists in our database, and if it does, an error message is returned.
A condition is set to verify whether the two password fields match. PHP is a case-sensitive language, so if the password field was written in capital letters and the confirm password field was written in small letters, it would return an error, but if they matched, it would filter the string and generate a password hash using the best method for hashing a password at the time. Every time the password hash() method is used, even though the words to be hashed are the same, a new hash is created.If there is no error, all data is saved in the database.
###Step 5- Creating Login form The login form is quite identical to the signup form, with only a minor change.
<?php require_once('dbconfig.php') ?>
<?php include('process.php') ?>
<?php
if(isset($_GET["session_expire"])) {
$err_message .= "Login Session is Expired. Please Login Again";
}
if(isset($_SESSION["user"])) {
header("Location:index.php");
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>Login-php login System</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Karla">
<link rel="stylesheet" href="assets/css/Registration-Form-with-Photo.css">
<link rel="stylesheet" href="assets/css/styles.css">
</head>
<body style="font-family: Karla, sans-serif;">
<div class="page">
<section class="register-photo">
<div class="form-container">
<div class="image-holder"></div>
<form method="post">
<h2 class="text-center"><strong>Login</strong> to your account.</h2>
<?php if ($err_message) : ?>
<div class="callout text-danger">
<p><?php echo $err_message; ?></p>
</div>
<?php endif; ?>
<div class="mb-3"><input class="form-control" type="email" name="email" placeholder="Email"></div>
<div class="mb-3"><input class="form-control" type="password" name="password" placeholder="Password"></div>
<div class="mb-3"><button class="btn btn-primary d-block w-100" type="submit" name="login">Login</button></div><a class="already" href="signup.php">Don't have an account? signup here.</a>
</form>
</div>
</section>
</div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
</body>
</html>
<?php
if(isset($_GET["session_expire"])) {
$err_message .= "Login Session is Expired. Please Login Again";
}
if(isset($_SESSION["user"])) {
header("Location:index.php");
}
?>
The code displayed above is used to determine whether the session expiration variable is included in the url in order to display a message indicating that the session has expired. If a login session has already been established, the second code disables access to the login page.
###Step 6 - Authenticating Login
I know you've been wondering why the code displayed in process.php
and dbconfig.php
so far doesn't match the one you have, but don't worry, they were omitted on purpose for the sake of explanation.
Process.php
if (isset($_POST['signup'])) { ...
}
//If the Login button is triggered
if (isset($_POST['login'])) {
//Checks if Both input fields are empty
if (empty($_POST['email'] || empty($_POST['password']))) {
$err_message .= 'Email and/or Password can not be empty<br>';
}
//Checks if email is valid
else {
$email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
if ($email === false) {
$err_message .= 'Email address must be valid<br>';
} else {
//Checks if email exists
$statement = $pdo->prepare("SELECT * FROM user WHERE email=?");
$statement->execute(array($email));
$total = $statement->rowCount();
$result = $statement->fetchAll(PDO::FETCH_ASSOC);
if ($total == 0) {
$err_message .= 'Email Address does not match<br>';
} else {
//if email exists save all data in the same column of the email in the row array
foreach ($result as $row) {
$stored_password = $row['password'];
}
}
}
//Checks Provided password matches the password in database if it does logs user in
if (password_verify($_POST['password'], $stored_password)) {
//setting the session variables
$_SESSION['user'] = $row;
//setting the session login time
$_SESSION['user']['loggedin_time'] = time();
header("location: index.php");
} else {
$err_message .= 'Password does not match<br>';
}
}
}
If the login button is triggered, the code checks if both the email and password fields are empty. If they aren't, it constructs a sql statement to check if the email exists. If it doesn't exist, an error message is displayed, but if it does, it retrieves the password associated with the email stored in the database and tries to match it with the provided password If no match is detected, an error message is returned, but if none is found, all of the data linked with the email from the database is stored in the super global array $_SESSION['user'].The login time and was then stored in the session array in unix format. The password verify function is used to unhash (for lack of a better word) the previously stored hashed password that was supplied during signup.
To retrieve any data from the database saved in the session array, use $_SESSION['user']['column name in database'].as an example, to obtain a user's password, you will use $_SESSION['user']['password'] or email, you would use $_SESSION['user']['email'].
A method to check if the session has expired what is included in the dbconfig.php
file.
<?php
$err_message = '';
$message = '';
// Change this to your connection info.
define('DBHOST', 'localhost'); //define your website host
define('DBUSER', 'root');//define your database username
define('DBPASS', '');//define your database password leave if no password is set
define('DBNAME', 'secure');// database name
// Try and connect using the info above.
try {
$pdo = new PDO("mysql:host=".DBHOST.";dbname=".DBNAME, DBUSER, DBPASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
// If there is an error with the connection, stop the script and display the error.
catch( PDOException $exception ) {
echo "Connection error :" . $exception->getMessage();
}
//Function to check if session is expird
function SessionExpire() {
//sets time for the session to expire. the time is in seconds
$session_duration = 600;
//checks if login time is set
if(isset($_SESSION['user']['loggedin_time']) and isset($_SESSION['user']['id'])){
if(((time() - $_SESSION['user']['loggedin_time']) > $session_duration)){
return true;
}
}
return false;
}
?>
After naming the function, a variable was set up to keep the session's duration in seconds. A condition is then constructed to verify if the time of login and the user id are both set. If the condition is fulfilled, it obtains the current time in unix format, subtracts it from the previously saved login time, and determines if it is greater than the session length. If the outcome is true, the function returns 'true,' but if it is false, it returns 'false.'
###Step 6- Creating the Home Page
The index.php file contains the following:
<?php
//includes database config
include("dbconfig.php");
// Check if the user is logged in or not
if (!isset($_SESSION['user'])) {
header('location: login.php');
exit;
}
// Check if the session has expired or not
if (isset($_SESSION['user']['id'])) {
if (SessionExpire()) {
header("Location:logout.php?session_expire=1");
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>Dashboard-php login System</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Karla">
<link rel="stylesheet" href="assets/css/Registration-Form-with-Photo.css">
<link rel="stylesheet" href="assets/css/styles.css">
</head>
<body style="font-family: Karla, sans-serif;">
<div class="page">
<section class="register-photo">
<div class="form-container">
<h2 class="text-center"><strong>User </strong> DashBoard.</h2>
<div class="callout text-primary">
Welcome <?php echo $_SESSION['user']["fullName"]; ?>. Click here to <a href="logout.php" tite="Logout">Logout.
</div>
</div>
</section>
</div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
</body>
</html>
When our users login in, the home page will be the first page they see. They will only view this page if they are logged in; otherwise, they will be sent back to the login page.
A condition to prevent access to the page if somehow the session array is not set (i.e if the user is not logged in) is followed by a function that checks if the session has expired and, if true, redirects the user to the logout.php file with a session expire variable defined in the url logout.php?session_expire=1
.Following that are some html codes for displaying the user's fullName
and a logout link. With the criteria shown above in Step 5, you may add any extra user data.
###Step 7- Creating A logout Script
It is extremely simple to construct the logout script; all you need to do is terminate the sessions that we established.
logout.php
<?php
ob_start();
session_start();
//includes data base config
include('dbconfig.php');
//Unset the session variable
unset($_SESSION['user']);
//destroy created session
session_destroy();
// Redirect to login page
header("location: login.php");
//check if session is expired
if(isset($_GET["session_expire"])) {
$url= "login.php?session_expire=" . $_GET["session_expire"];
//Destroy session if time's out
unset($_SESSION['user']);
//redirect to login if expired
header("Location:$url");
}
Initialize sessions and destroy them (if any) before redirecting the user to the login page; we need sessions to identify whether or not the user is logged in, thus deleting them means the user is not logged in. A condition that checks if the session has expired and redirects it to the login page if it has.
##Conclusion You should now have a good knowledge of how a PHP and MySQL login system works. You are allowed to copy the source code and use it in your own projects. The system will then be enhanced with additional security measures. which I will later develop tutorials for. If you enjoy our teaching method, please like and comment below. Don't forget to follow us and share the post; doing so will allow us to develop more lessons in the future.