Login
Signup as a Tutor

As a tutor you can connect with more than a million students and grow your network.

User Authenication In PHP: Some Advanced echniques

Dheeraj Kushwaha
25/07/2017 0 0

User Authentication in CodeIgniter: Goals

  • Security: We want our login system to be secure, we want to do everything we can to stop people’s accounts being hacked into.
  • Tracking: We’d like to know who’s logged in, when they were last active and what they’ve looked at.
  • Efficiency: We want to include features to ensure the system is efficient and doesn’t slow everything down.

A Bit of Theory:

Before we jump in and start coding, it’s important to discuss some theory on how we can achieve the above goals.

Security:

Firstly, on the security side of things we need to decide how we will determine if a user is logged in. Personally I prefer to do this using a cookie rather than a PHP Session. Reason being you can keep users logged in for longer periods of time with cookies (ie  provide a “remember me” option). However including any user information in the cookie itself is a security risk, because anybody can access the cookies in their browser and find the data stored. This is not just bad on shared computers  if you’re saving someone’s user id for example, what’s stopping someone guessing another user’s user id and setting their own cookie?

We will be using a database table along with a cookie to determine if a user is logged in. The cookie itself will hold just a hashed string of characters that reference a field in the database table. If a session exists the user is logged in, if not we can delete the cookie and get them to log in.

Taking this further, it makes sense to check other things at this stage too. The user’s last login can be saved in the database table for example then if they’ve not been active for a set amount of time we can force log them out. We can also track a user’s IP address, and if the user’s current IP address is different log them out. It could be perfectly innocent as a lot of users will not have a static IP address, but for the sake of security getting them to log in again when it does change isn’t a big loss.

Tracking:

This database table leads nicely into the tracking we want to do. Every time a user visits a page on your application, we will perform the above checks to ensure a valid session exists. At the same time we can update their last active date. You could  if you wanted to go further have another database table that logs pages viewed or actions taken. Doing so would give you a lot of data to run statistics on, providing useful metrics for future development or marketing purposes.

Efficiency:

With this kind of tracking however, we need to be careful with performance. It doesn’t take an expert to realise that if you’re inserting a new row in a table every time a user visits a page your table is going to grow very big very fast. Some tweaks and carefully inserting only when you need to can help here, along with regularly clearing data you don’t need any more.

So that’s the theory out of the way, time to start coding!

Database Tables:

As you will have gathered by now, everything revolves around a database table for user sessions. Here’s an example table definition for user_sessions:

PHP
CREATE TABLE `user_sessions` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `hash` char(40) NOT NULL DEFAULT '',
  `users_id` int(11) NOT NULL,
  `created_date` datetime NOT NULL,
  `last_active` datetime NOT NULL,
  `inactive` tinyint(1) NOT NULL DEFAULT '0',
  `ip` varchar(45) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

As you can see the table references the users table with users_id. The hash field will hold a 40 character SHA1 hash which is saved in the user’s cookie. We save the date the user first logged in with this session in created_date and the last_active date holds when the user was last active in the system. The inactive field is set to 1 when a user is logged out so we know it’s an old session. Finally the ip field will hold the user’s IP address.

If we wanted to track pages a session visits here’s an example table definition for sessions_track:

PHP
CREATE TABLE `sessions_track` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `sessions_id` int(11) NOT NULL,
  `uri` varchar(255) NOT NULL DEFAULT '',
  `last_visited` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

In this table we reference the sessions table with sessions_id. The URI of the page visited and the last_visited date. We don’t really need to capture any more than this, and the smaller this table is the better!

Finally, here’s an example users table:

PHP
CREATE TABLE `users` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(55) NOT NULL DEFAULT '',
  `email` varchar(100) NOT NULL DEFAULT '',
  `password` char(40) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

I’ve kept this to the bare minimum, your system might require all manner of user information, but for the purposes of this article just the user’s name, email and a password are only really required.

MD5 vs SHA1:

You’ll notice the user’s password and the hash in the tables above are 40 character SHA1 hashes rather than 32 character MD5 hashes. Put simply, SHA1 is more secure than MD5. The actual reasons why are outside the scope of this article, but with it being just as easy to hash a string using SHA1 in PHP as it is MD5, there’s no reason not to use SHA1.

PHP
sha1($password);

User Sessions Model:

To achieve our goals we’ll be creating a Model. The Model will hold all the functions our application will need. To start with, let’s create the skeleton of the class first:

PHP
class User_sessions_model extends CI_Model {

        

}

Login Function:

PHP
public function login($username, $password) {

	// Is there a valid user?
	$this->db->select('id');
	$this->db->where('email', $username);
	$this->db->where('password', sha1($password));
	$query	= $this->db->get('users');
    $user   = $query->row_array();

	if ( $user['id'] ) {

		// Is there an open session for this user?
		$this->db->select('id');
		$this->db->where('users_id', $user['id']);
		$this->db->where('inactive !=', 1);
		$query		= $this->db->get('user_sessions');
	    $session   	= $query->row_array();

		if ( $session['id'] ) {

			// Close the session
			$this->destroy_session($session['id']);

		}

		// Create the new session
		$newsession['users_id']	= $user['id'];
		$newsession['ip']		= $_SERVER['REMOTE_ADDR'];
		$this->create_session($newsession);

		return true;

	} else {

		return false;

	}

}

The first part of this function is pretty standard stuff. We check to see if a user exists with the email and password passed.

If they do we check the sessions table for an active session. If there is an active session for the user we call destroy_session to close it down – we’re about to make a new one and we don’t want more than 1 active session open at once.

Then, we call create_session which creates a new session for us, and sets the relevant cookies.

Create Session Function:

PHP
public function create_session($data) {

	// Set additional data
	$data['created_date']	= date('Y-m-d H:i:s');
	$data['last_active']	= date('Y-m-d H:i:s');
	$data['inactive']		= 0;
	$data['hash']			= sha1($data['users_id'].time());

	// Perform the insert
	$this->db->insert('user_sessions', $data);

	// Create cookie
	set_cookie('user_login', $data['hash'], 0, 'your-domain');

}

This function creates a new session. All of the required data should be passed in the $data parameter as an array. Note the keys in the array match the database field name so we can use CodeIgniter’s build in database class to perform the insert for us. Then we create the user’s cookie.

This is also where we create the hash. You can do this a number of ways, but it’s a good idea to combine a number of pieces of information unique to the member with a random string or the current time. It’s also a good idea to use a different combination of values on each application you build.

Session Check Function:

PHP
public function session_check($hash) {

	// Is there a session for this hash?
	$this->db->where('hash', $hash);
	$this->db->where('inactive !=', 1);
	$query		= $this->db->get('user_sessions');
    $session   	= $query->row_array();

	if ( $session['id'] ) {

		// Check the session isn't more than 30 days old and the ips match
		if( ( strtotime($session['last_active']) < strtotime('-30 days') ) || $session['ip'] != $_SERVER['REMOTE_ADDR'] ) {

			$this->destroy_session($session['id']);
			return false;

		} else {

			// Update the last active date to now
			$this->db->set('last_active', date('Y-m-d H:i:s'));
			$this->db->where('id', $session['id']);
			$this->db->update('user_sessions');

			// Track the URI
			$trackdata['sessions_id']	= $session['id'];
			$trackdata['uri']			= $this->uri->uri_string();
			$this->track_uri($trackdata);

			// Return the session
			return $session;

		}

	} else {

		return false;

	}

}

This function is used when a user visits a page. The hash stored in their cookie is passed in the $hash parameter, and we check to see if there’s a session that isn’t inactive in the database. If there is an active session, we perform a check to see if their session is too old, and a check to ensure the user’s current IP address matches the one in the session. If any of the checks fail we return false and call destroy_session.

Otherwise, we update the last active date and we call track_uri to track the URI the user has visited. Finally we return the session itself.

Destroy Session Function:

PHP
public function destroy_session($session_id) {

	// Mark as inactive in database
	$this->db->set('inactive', 1);
	$this->db->where('id', $session['id']);
	$this->db->update('user_sessions');

	// Destroy the cookie
	delete_cookie('user_login', 'your-domain');

}

Here we mark a session as inactive and destroy the user’s cookie.

Track URI Function:

PHP
public function track_uri($data) {

	// Set additional data
	$data['last_visited']	= date('Y-m-d H:i:s');

	// Perform the insert
	$this->db->insert('sessions_track', $data);

}

This function inserts the current session, URI and date into the sessions_track table.

Usage Examples:

In the above user sessions model there are a number of functions that provide us with our secure user logins system – but how do we use them? Here are a few examples:

Handling a login form:

PHP
public function login($username, $password) {

	// Is a user already logged in?
	$users_data = $this->users_model->session_check($this->input->cookie('user_login'));

	if ( $users_data['id'] ) {
		redirect('members');
	}

	// Login form submission
	if ( $this->input->post('frm_submit') ) {

		$users_id = $this->users_model->login($this->input->post('email'), $this->input->post('password'));

		if ( $users_id ) {

			redirect('dashboard');

		} else {

			$data['error_message'] = 'Login failed, please check your username and password are correct and try again';

		}

	}

	// Load view
	$this->load->view('login', $data);

}

Notice how the login form also checks for a logged in user. If there is one it redirects to the members section – we don’t want them to log in twice!

Checking if a user is logged in

PHP
public function members_only_page() {

	// Is a user already logged in?
	$users_data = $this->users_model->session_check($this->input->cookie('user_login'));

	if ( !$users_data['id'] ) {
		redirect('login');
	}

	// User is logged in and valid - continue with members only content

	// Load view
	$this->load->view('members_only_page', $data);

}

Typically, this will be done on every controller function. The if statement checking if a user is logged in and redirecting if not can be removed for pages that aren’t member’s only – this way you can display a login link if a user is not logged in for example.

Handling logging out:

PHP
public function logout() {

	// Is a user already logged in?
	$users_data = $this->users_model->session_check($this->input->cookie('user_login');

	if ( $users_data['id'] ) {
		$this->users_model->destroy_session($users_data['id']);
	}

	redirect('login');

}

Auto load the model:

PHP
// config/autoload.php

$autoload['model'] = array('User_sessions_model');

For most systems, this functionality will be applicable to every page. Therefore it makes sense to autoload the model in order to have access to it at all times.

Performance Tweaks:

Smart inserting into sessions_track:

In our tracking function we could be a bit smarter with the data we insert. Firstly we could check to see if the same track request has happened within the last few seconds. This simple throttling will stop multiple requests erroneously filling the table up. Secondly, tracking the time of every request to a URI might be a bit overkill, so we can check to see if the session has already tracked the URI, and if it has just increment the visits field. Of course, this does require the addition of a visits field in the database table too.

Cleaning up data we don’t need in sessions_track:

Via a cron job, you could remove all data that is more than 30 days old (or any age depending on how long you want to track stats for). If you wanted to be really clever, you could even export this data out to a csv and save it on the server before deleting, so you have historical data at your disposal if you ever did want to run stats on large amounts of historical data.

Utilising Database Indexes:

The addition of a few carefully placed indexes can dramatically improve the performance of these tables. Typically any field a WHERE query is performed against.

I hope this information will help you to put better security to the user management  of your website

0 Dislike
Follow 0

Please Enter a comment

Submit

Other Lessons for You

Handbook of websites for Website Developers/Designers (software professionals)
Know the trending languages(past & present) and their comparision with other languages: @ https://www.tiobe.com/tiobe-index/ Found an interesting website? identify the technologies used to build website...
Top Programming Languages 2017: Every Beginner Should Learn
Every year a plethora of job opportunities are being created for skilled programmers. So if you are thinking of honing your coding skills it is really a bright idea. But with so many programming languages...

Ace Web Academy | 24/08/2017

2 0
0
How to Create A Master Page Template In PHP?
A master page template is essential to give a consistent look and feel to any website having multiple pages. It is quite easy to create a master page template in PHP using Dreamweaver. Let’s have...

Karmick Institute | 10/08/2017

0 0
0
Happiness Or Satisfaction: How To Quit Your Day Job?
Four years ago on a sunny April morning, I slinked into my new office building, suit slightly too big, 24-years-old and clueless. It was my first day working at a large, prestigious Organization. The first...

Rohit Raj Verma | 29/07/2017

0 0
0
Be Carefull While Uploading A file To Server Using PHP
With PHP, it is easy to upload files to the server. However, with ease comes danger, so always be careful when allowing file uploads. 1. Configure The "php.ini" File: First, ensure that PHP is configured...

Praveen Tyagi | 19/07/2017

0 0
0

Looking for PHP Classes?

Find best PHP Classes in your locality on UrbanPro.

Do you offer PHP Classes?

Create Free Profile »
Sponsored

Find Best PHP Classes?

Find Now »