18 August 2006

Make PHP session more secure

Yes, a post again!

The past days I have been busy creating a new login system to replace the old one. Since there are PEAR classes for PHP I try to use them if I can. But you do have to program something yourselves.

The PEAR Auth class is session based. It relies on the persistence of the session over stateless HTTP by identifying the session (which is kept server-side!) by a client-provided session ID (SID). The client sends its SID each request and the server looks up the session identified by this SID and can add, change or delete session variables. If a user is authenticated a session variable ("registered") is set to true.

What is the problem then? If someone can get your SID he can get in your session without knowing your password and maybe even change your password. This is called: session hijacking. Wikipedia has a small article on it. Another method to get into your session is to setup a session with a specific SID by linking to the site with that SID. This is called: session fixation. About this subject Wikipedia is more complete.

I have tested something around with the PHP function session_regenerate_id in PHP 5.1.2. Some references on the web seemed wrong. While testing with session cookies on calling session_destory and session_regenerate_id subsequently I was using an empty session with the old SID afterwards. The way Nicolas describes in the user contributed notes by this function seems the only way to destroy the old session and going on with the new one.

After all research I added some code to my login system to prevent session fixation and session hijacking. Nowhere in the documentation for the PEAR Auth class I found anything about these themes. But while writing this post and digging into the source to find which variable is set I found the Auth class is trying to prevent session fixation and session hijacking already!

A snip from the Auth getAuth method (starting with line 824 in version 1.3.0):

// check for ip change
if ( isset($this->server['remote_addr'])
&&
$this->session['sessionip'] != $this->server['remote_addr']) {
// check if the ip of the user has changed, if so we
// assume a man in the middle attack and log him out
$this->expired = true;
$this->status = auth_security_breach;
$this->logout();
return
false;
}

// check for useragent change
if ( isset($this->server['http_user_agent'])
&&
$this->session['sessionuseragent'] != $this->server['http_user_agent']) {
// check if the user-agent of the user has changed, if
// so we assume a man in the middle attack and log him out
$this->expired = true;
$this->status = auth_security_breach;
$this->logout();
return
false;
}

Again I can say: "Use PEAR! It is better than the documentation shows you!"