Client Clones and Server Sessions

By Tony Marston

30th April 2005

Introduction
Session Identities
Session Names and Session Cookies
Dynamically changing the Session Name
Starting a new session

Introduction

The session handling functions in PHP are designed to save state between different requests from the same client. This works well if the client device is accessing the application via a single browser window, but what happens if the user decides to open up several browser windows and attempts to access the same application through multiple windows at the same time? Unless each browser window on the client device can pass a separate identifier to the application running on the web server, they will all be treated as belonging to the same session. This means that the server will not be able to keep the activities in one browser window separate from the other, which may lead to unexpected results.

This problem comes to light in those browsers which make it easy for the user to open up a new window which is a complete copy or clone of the current window. The cloned window shows the same URL as the original window, and shares the same browser history and cookie data. The cloning process can be achieved as follows:

It is therefore possible for the user to create several cloned browser windows on the client device, and then to visit different pages within the same application in each of those windows. The user may assume that the activities in one window will not have any effect on the activities in any other window, and may be disappointed to learn that this assumption is incorrect.

This type of behavior does not occur with traditional desktop applications as it is not possible to create cloned copies - it is only possible to start up a fresh instance of the application, and each instance will automatically have its own separate memory.

Session Identities

It is important to note that within the HTTP protocol there is no mechanism that can be used successfully to tie a particular session to a particular browser window on a particular device. It is therefore the responsibility of the web application to employ its own mechanism. PHP solves this problem by allocating a new session identifier (session_id) to each new session, and passes back that value in the response. A subsequent request from the same client, provided that it supplies the same session id, will automatically be re-united with the session data that was previously stored using that id.

Note here that the identity of the client submitting the request is irrelevant - it is all controlled by the session identifier:

Session Names and Session Cookies

The standard mechanism for PHP to supply and retrieve session identifiers is to use session cookies. These are called "session" cookies as they exist in the client's memory only for the duration of a particular browser session. They are not written to disk, therefore as soon as the browser session is terminated any session cookies will be dropped from memory and will cease to exist. If you have a web browser such as Firefox or Opera you will be able to examine any of these session cookies.

Each cookie is in the format name=value. It is used by PHP via the $_COOKIE array to represent session_name=session_id. The default session name used by PHP, as defined in the php.ini file, is PHPSESSID. The session identity is a randomly generated 32 character string, so it is possible to see something like the following in your session cookies:

PHPSESSID=af58bbf6b4d1298614583edff5b0ab8d

In some cases either the web application or the client browser will decide not to use session cookies, in which case this information may appear:

It is possible to change the value of the session name in the following ways:

Dynamically changing the Session Name

Simply using the session_name() function to change the session name will not solve the problem. There are additional items that must be taken into consideration:

I have created code which solves this problem, and I include here for your edification.

The following code is run at the start of each script so that it can extract the session name from the contents of either the GET or POST request.

global $session_name;
if (isset($_REQUEST['session_name'])) {
    // use session name passed via $_GET or $_POST
    $session_name = $_REQUEST['session_name'];
} // if

The following code will use the existing session name (if supplied) or generate a new one.

// get details from any previous session
if (isset($session_name)) {
    // use existing session name
} else {
    // assign new session name
    $session_name = getNewSession('menu');
} // if
session_name($session_name);
session_start();

The session_name() function will tell PHP to use this name in place of any previously supplied by any php.ini or .htaccess file.

The session_start() function will extract the session id that goes with this name and use that to retrieve previously-stored data and load it into the $_SESSION array, or it will generate a new id and start with an empty $_SESSION array.

The custom function getNewSession() is defined as follows:

function getNewSession ($prefix='menu')
// create a new session name using $prefix + a number.
{
    // step through numbers 0-99
    for ($i = 0; $i <= 99; $i++) {
        $session_name = $prefix .$i;
        if (!array_key_exists($session_name, $_COOKIE)) {
            break;
        } // if
    } // if
    
    return $session_name;

} // getNewSession

All that is required after this point is to ensure that the new session name is built into every subsequent request from that client. This is done as follows:

Starting a new session

As I stated earlier it is impossible for the server to know that a new browser window has been created on the client device, so it is up to the user to instruct the application to start a new session. To do this each page must include a new session hyperlink similar to the following:

<a href="HTTP://whatever/script.php?action=newsession&session_name=menu0">new session</a>

When this is pressed it will execute the following application code on the server:

  if (isset($_GET['action'])) {
      if ($_GET['action'] == 'newsession') {
          // get and register a new session name
          $session_name = getNewSession('menu');
          session_name($session_name);
          // generate a new id to go with this name
          session_regenerate_id();
          // save this session data NOW!
          session_write_close();
          // now restart the current script
          $location = 'http://' .$_SERVER['HTTP_HOST']
                     .$_SERVER['PHP_SELF']
                     .'?session_name=' .session_name();
          header('Location: ' .$location); 
          exit;
      } // if
  } // if

This is executed after the previous code, so the $_SESSION array has already been populated with the existing session data. The session_name() and session_regenerate_id() functions have the effect of telling PHP at the server end to store this session data with a new session id, and the browser at the client end to refer to the session with a different name.

As a result of this operation the user should see the current page being refreshed, but with a different session name in the URL.

Although the new session starts with the same session data as the session from which it was cloned, from this point on the activities in each of the browser windows will be kept separate.

If you examine the cookie data after this operation you should see something like the following:

menu0 = a3a73430fe22e103638bf916b7fffe3e
menu1 = 726ffac3032f201350331bb7955d1848

Note that this procedure will allow up to 100 different sessions in different browser windows to exist on any one client at any one time. That should be more than enough for anybody.


© Tony Marston
30th April 2005

http://www.tonymarston.net
http://www.radicore.org

counter