One of the (pure PHP) ways PHP offers for saving user data across several pages are Sessions. They come in different flavours: at first, there’s the one that makes your URL ugly, but makes them usable for all users. Secondly you can use cookies. That’s much nicer, but doesn’t work for all browsers. Surf2me.nl uses the latter.

Sessions

But what are sessions? It’s something like a cookies: a variable that belongs to a specific user. The disadventage of cookies is that they can be changed by the user and twenty variables is a bit too much to save at the user’s side. That’s why sessions are created by PHP. Sessions are nothing more or less then a set variables linked to a specific key, which is saved at the user’s side.

This may sound weird, but it isn’t. For example, the variable ‘username’ can be given the value ‘user’ and the variable ‘password’ the value ‘pass’. At the server, this is saved:

/tmp/session.abcdef1234567890.txt
 username = user
 password = pass

Now, at the user’s side, a cookies is created. (It is also possible to do this using GET- and POST-variables, but this creats URLS like index.php?PHPSESSID=abcdef1234567890, which is, in my opinion, not nice.) The cookies has this value:

PHPSESSID         = abcdef1234567890

Now, PHP understands this is an indicator for a session and fills the super global $_SESSION with the session values.

<? echo $_SESSION['username'] . " - " . $_SESSION['password']; ?>

The previous code gives as result:

user – pass

In fact, I can’t think of any reason not to use sessions. Sessions are always available and can always be set. It doesn’t require cookies and the values aren’t adjustable or readable by users. It requires little attention from the developer and sessions are very powerful.

Configuration

As noted before, sessions require little configuration. Especially the garbage collector of PHP isn’t easy to understand. The garbage collector, or gc, cleans the sessions after a while, since it isn’t required to keep all sessions in the memory.

Before we discuss this, first the configuration of Surf2me.nl. You can put this in an .htaccess file in the website root, but you can also use ini_set.

php_value session.save_path        /.../sessions
php_value session.cookie_lifetime  7200
php_flag  session.use_cookies      on
php_flag  session.use_only_cookies on
php_flag  session.auto_start       on
php_value session.name             session
php_value session.gc_maxlifetime   86400
php_value session.gc_probability   1
php_value session.gc_divisor       100

Of course, this aren’t all available variables, but it are the variables I need. I will discuss them below, but if you want more information, refer to the PHP Manual.

session.save_path

For the safe path, I don’t use /tmp, the default of PHP, but my own directory. At first, on a shared server it’s possible someone else can read all files in /tmp and therefore all session data. This way you protect your own session data from others. Of course this should be a folder which is readable by everyone (for example, a folder in public_html). Secondly, it is possible that different garabage collectors clean up your files.

Note that this path should be writable by PHP, so you most likely have to chmod that directory.

session.cookie_lifetime

With this variable, you define how long the session cookie should stay on the remote computer. A value of zero tells the browser to remove the cookie if the browser is closed. Every other positive value is the number of seconds since the start of the session. I’m not sure, but the cookie won’t be lengthened automatically. To do this, I use this code:

setcookie(session_name(),session_id(),time()+7200,'/');

session.use_cookies

Very intuitive, if you want to use cookies, this is on.

session.use_only_cookies

This one is also very clear: if you only want to use cookies, this is on.

session.auto_start

If this one is on, it isn’t necessary to start the session manual in every script. If this one is off, you have to put session_start() in every script that uses sessions. If this is on, this is implicitly placed in every script that uses a session.

session.name

This is the name of the session. It defaults to PHPSESSID, as used in the example above. Surf2me.nl uses ‘session’ as name, but you can use whatever you like.

Garbage Collector

The most confusion settings of the sessions are those of the Garbage Collector. The Garbage Collector (or GC) cleans your old session files and removes them. There are three settings important for the GC: session.gc_maxlifetime, session.gc_devisor and session.gc_probability.

session.gc_devisor and session.gc_probability

To prevent that the GC will clean up everytime a page is generated (to be more precise, when a session is created), you can set session.gc_devisor and session.gc_probability. The chance the GC gets active is session.gc_probability out of session.gc_devisor.

So, if you want to get a visit of the GC every 1 out of 100 times, you set session.gc_probability = 1 and session.gc_devisor = 100. With these settings, the GC will give a visit every 100 times a page is generated. If you have a big site, you can decrease this value (to 1:1000 for example), because the GC will get active enough times.

session.gc_maxlifetime

The setting session.gc_maxlifetime defines the life span of a session. In other words, the minimum time a session exist. The time, in seconds, is calculated from the latest adjustment to the session file (in PHP <= 4.2.2 this is the latest access to the file). If this time has been exceeded, the GC is allowed to remove this file. To prevent the file to be removed to early, every time I read from the session, I adjust one variable.

One final note to maxlifetime: if you have two different GC’s on the same folder, the shortest maxlifetime will be effective. PHP can’t make any difference between two different maxlifetimes, so if a GC is initiated, it will use its own maxlifetime.

Utilization

PHP fills the super global $_SESSION with all variables it can load when session_start() is executed (or the session handler is initiated if session.auto_start is on). If you want to change any variable, just use $_SESSION['name'] = 'value';.

Security

One final note on security: it is possible for hijackers to act like another user, by copying the session name and value to its own computer. You can prevent such hijacking by saving the user IP in the session and checking if this value matches with the current user’s IP. If these don’t match, you don’t accept the session.

Note that this doesn’t work if an user is behind a NAT, because multiple users will have the same IP. You might also want to safe data like the user agent, but remember that this information is very easy to spoof.

However you should be able to trust all values in the $_SESSION super global, don’t! It is always possible that these are spoofed by someone with server access.

This item appeared earlier in Dutch on my previous blog.