PHP How to limit number of simultaneous sessions per user account

Soumaya Hachem
Soumaya Hachem used Ask the Experts™
on
Hi, I want prevent user to connect at the same times in two computer.Please Help

I try this but it not working


session_start();

 if(!(isset($_SESSION['login']) && $_SESSION['login'] != ''))
 {
 session_destroy();
       $redirect_to = isset($_REQUEST['redirect_to']) ? $_REQUEST['redirect_to'] : '/wordpress';
        $location = str_replace('&', '&', wp_logout_url($redirect_to));;
        header("Location: $location");
}

Open in new window

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Most Valuable Expert 2011
Top Expert 2016

Commented:
Seeing this wp_logout_url() makes me wonder, is this a WordPress application?
Most Valuable Expert 2017
Distinguished Expert 2018

Commented:
You can't do it by destroying the session - a session is unique to a computer.

What if the user signs in on computer A but does not logoff. Computer is destroyed by visiting aliens. Now user goes to computer B - as far as the server is concerned the user is logged out.

Point being - how do you want to treat the second login attempt

a) Block it
b) Allow it but destroy any existing sessions that may already exist for the user?
Most Valuable Expert 2011
Top Expert 2016

Commented:
There really is no good solution to this issue, as we have tried to explain before.

You can keep a record of who signed in.  That is the easy part.  The hard part is determining whether they are still using the computer.   Unless they go through a formal logout process, you have no way of knowing where they are or what they are doing.  In fact, it is impossible for you to know if I sign in to your system and then walk away and let Julian use my computer.  You know I signed in, but you cannot know that it's still me at the keyboard.

You can track the IP address of a user.  But then you face the issue that you may have more than one user behind a given IP address.  This is very common in schools and office environments, where the internal network presents a single IP address, and the students or workers share the bandwidth of the internet connection through a single IP address.  So it is not feasible to reject multiple users from the same IP address.

You can reject duplicate sign-in requests that are not from the same IP address.  But then you face the issue that some ISPs use proxy servers and are constantly changing the IP address of the (human) client's request.  So if I'm coming to you from 69.65.9.210 when I log in, what will you do when my next request comes from 69.65.7.211?  Am I using more than one computer?  You can't know.

About the only design I can think of that might work would require a special session handler.  When the client logs in, his session information is stored in your database (see http://php.net/manual/en/session.customhandler.php), and an entry is simultaneously stored in a database table of "who_is_logged_in" now.  You will reject any login for clients who are already logged in, according to the contents of the "who_is_logged_in" table.  Every time your server receives a request, it will run the general site-wide garbage collector.  Each time garbage collection is called, you will look at the last-used time of all of the entries in the "who_is_logged_in" table.  If you find that a client has passed the time-out period (whatever period you choose) without any inactivity, you will destroy all of their session data and remove their entry from the "who_is_logged_in" table.  This means that the cookie the return on the next request will find no data in the session.  When that happens, use setcookie() to eliminate their session cookie.  They can log in again after the time-out period, but not before it expires.  Obviously, if they go through a formal log-out process, you will do the same invalidation process to destroy the session, eliminate the cookie, and remove them from the "who_is_logged_in" table.

The lockout expires on its own terms when the time-out period has past.  This means that if I were to log in at home and forget to log out before I drove to the office, I could still log in at the office, assuming it takes me more than the time-out period to get to the office.  Common sense would argue that the time-out period should be short.  PHP's default timeout is 24 minutes of inactivity.

Whatever you do with this idea, you are going to make life an absolute hell for developers who are trying to build on one machine and test on an adjacent machine, so you probably want to build some kind of back-door into the system that allows authorized clients to bypass the "who_is_logged_in" restrictions.

One final thought - what if there is a database error, and someone does not get purged correctly from the "who_is_logged_in" table?  They will never be able to log in again?  You will want to have some kind of process, probably in garbage collection, to ensure that this condition cannot happen.

HTH, ~Ray
HTML5 and CSS3 Fundamentals

Build a website from the ground up by first learning the fundamentals of HTML5 and CSS3, the two popular programming languages used to present content online. HTML deals with fonts, colors, graphics, and hyperlinks, while CSS describes how HTML elements are to be displayed.

Most Valuable Expert 2017
Distinguished Expert 2018

Commented:
A slight variation on Ray's suggestion - doesn't require garbage collection.

The simplest solution is to assume the next session access ends the last. The only watch-it here is if there is data that needs saving in the other session but that can be achieved with AJAX and timeouts that can push unsaved data to the server.

Process

Sessions (as Ray said) are stored in the DB. Session identifiers are stored in $_SESSION (a GUID for example).

* When a user accesses the system check $_SESSION for a key
* If there is a key check the session database for a record
* If a valid record is found set the user's state to logged in
* If no valid session is found send user to login screen.
   (Sessions are found by userid or session key - so a previous session can be located after logon to a new machine)
* Validate user
* Update / create session record in the DB with new Session Key (If garbage collection is required it is done here - remove all records for the user except new one - even if a db error happens the next login will cleanse the db). Personally I would look at recycling the session but obviously that is solution specific.

The last step can vary depending on whether you want to recycle session data from previous sessions Or start from scratch.

If the user goes back to previous computer they were logged onto they will require to re-authenticate.

A variation on the above would be to notify the user at step 3 that they have an open session and request they first terminate that session. Personally I don't like this approach as it might be difficult to access the previous session.
Most Valuable Expert 2011
Top Expert 2016

Commented:
doesn't require garbage collection
That sounds like a recipe for a security issue.  There is no sessionhandler I have ever seen (and I've seen quite a few) that does not use a garbage collector.  I think I would need to see a code example before I could endorse that idea.

I also think that any design along these lines (preventing clients from using two computers) should minimize the information kept in the session.  This might be OK for tracking student logins, but it would be a horrible design for a shopping cart.

Some of the theory and practice of the standard PHP sessionhandler is available in this article.
https://www.experts-exchange.com/articles/11909/PHP-Sessions-Simpler-Than-You-May-Think.html

Author

Commented:
favorite
How can I forbid a user from logging in with the same account in two different browsers at the same time.

session_start();
// set time-out period (in seconds)
$inactive = 30;
if( is_user_logged_in() ) {

if (isset($_SESSION["timeout"])) {
    // calculate the session's "time to live"
    $sessionTTL = time() - $_SESSION["timeout"];
    if ($sessionTTL > $inactive) {
        session_destroy();
       $redirect_to = isset($_REQUEST['redirect_to']) ? $_REQUEST['redirect_to'] : '/wordpress';
        $location = str_replace('&', '&', wp_logout_url($redirect_to));;
        header("Location: $location");
    }
    else{
        wp_die('<h1>User is login! </h1>', '', array( 'back_link' => true ));    
    }
}
}
Most Valuable Expert 2017
Distinguished Expert 2018

Commented:
That sounds like a recipe for a security issue
How about this (will work for either two different machines or two different browsers).
This code would run before access to any page is given
<?php
define('SESSION_TIMEOUT', 86400 * 30);
$cookie = isset($_COOKIE['myAppCookie']) ? $_COOKIE['myAppCookie'] : false;
if (!isValidSession($cookie)) {
   header('location: login.php');
}

function isValidSession($id) 
{
  $result = false;
  if ($id) {
     $result = getSessionById($id);
     if ($result) {
       setcookie("myAppCookie",$id,time()+TIMEOUT);
     }
  }

  return $result;
}

Open in new window

Login processing code (assumes form)
<?php
$result = false;
$error = '';
if ($_POST) {
  // EXTRACT AUTH VALUES
  $username = isset($_POST['username']) ? $_POST['username'] : false;
  $password = isset($_POST['password']) ? $_POST['password'] : false;

  // ASSUME THE WORST. FROM HERE ON IN WE ARE VALID OR NOT
  // CAN'T GET HERE ON FIRST VISIT. IF WE SUCCEED WE NEVER USE
  // THE ERROR SO NO NEED TO CLEAR IT
  $error= 'invalid username or password';
  if ($username && $password) {
    $result = getUserAccount($username, $password);
    if ($result) {
      setcookie("myAppCookie",$result->id,time()+TIMEOUT);
      $session = getSessionByUserName($username);
      // RECYCLE THE SESSION WITH A NEW ID
      if ($session) {
        $session->id = $result->id;
        saveSession($session);
      }
      else {
        // NO PREVIOUS SESSION EXISTS
        $session = createSession($result);
      }
    }
  }

  if ($result) {
      header('location: secure.php');
    }
}
// WE ARE EITHER ON FIRST VISIT TO THE PAGE
// OR SOMETHING WAS WRONG WITH CREDS
require('login.html');

Open in new window


The above is UNTESTED code - just an out of my head example on how you can reuse sessions and prevent logon from two different computers. Database is self "cleaning"
Most Valuable Expert 2011
Top Expert 2016

Commented:
One more time, please...

Is this a WordPress application?

Author

Commented:
this is a web site wordpress
Most Valuable Expert 2011
Top Expert 2016

Commented:
Thanks, WordPress makes a huge  difference in the way you would approach such a thing.  I added the Wordpress Zone to the question.  Maybe we can get a WP expert to come by and consider the requirement.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial