Updated on 01/22/2009 There is no such thing as a 100% secure anything in this world of hackers/counter hackers. Especially when it comes to the world of web development. One of the many methods hackers use to infiltrate web applications is through session fixation. Session fixation is a way that hackers can use to gain unauthorized access to another users login. So how do we guard against this?
The Code
Here is a class that I developed that originated from Vagharshak Tozalakyans’ secure session class:
<?php class session { /** * Add a parameter with a value to a session * * @param string $name * @param mixed $value */ static function add_param($name, $value) { session_register($name); $_SESSION[$name] = $value; session_write_close(); } /** * Get a named parameters value * * @param string $name * @return mixed */ static function get_param($name) { if (isset($_SESSION[$name])) { return $_SESSION[$name]; } else { return false; } } /** * Delete a parameter with its value from a session * * @param string $name */ static function delete_param($name) { $_SESSION[$name] = ""; session_unregister($name); } /** * Fully destroy a session and all its values * */ static function destroy() { $_SESSION = array(); session_destroy(); } /** * Check to see if the session is scure * * @return bool */ static function check() { if (config::session_timeout == 0) { return (isset($_SESSION['ss_fprint']) && $_SESSION['ss_fprint'] == self::_Fingerprint() && ($_SERVER['HTTP_REFFERER'] != '' ? (strpos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) !== false ? true : false) : true)); } else { $date = date('m/d/Y H:i'); if (intval(strtotime($date)) < intval(self::get_param('timeout'))) { self::add_param('timeout', intval(strtotime($date)) + (60*intval(config::session_timeout))); return (isset($_SESSION['ss_fprint']) && $_SESSION['ss_fprint'] == self::_Fingerprint() && ($_SERVER['HTTP_REFFERER'] != '' ? (strpos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) !== false ? true : false) : true)); } else { return false; } } } /** * Starts the secure session * */ static function start_secure_session() { self::add_param('ss_fprint', self::_Fingerprint()); self::_regenerate_id(); if (config::session_timeout > 0) { $date = date('m/d/Y H:i'); self::add_param('timeout', intval(strtotime($date)) + (60*intval(config::session_timeout))); } } private function _Fingerprint() { $fingerprint = config::session_salt; if (config::check_browser) { $fingerprint .= $_SERVER['HTTP_USER_AGENT']; } if (config::check_ip_blocks) { $num_blocks = abs(intval(config::check_ip_blocks)); if ($num_blocks > 4) { $num_blocks = 4; } $blocks = explode('.', $_SERVER['REMOTE_ADDR']); for ($i=0; $i<$num_blocks; $i++) { $fingerprint .= $blocks[$i] . '.'; } } self::_regenerate_id(); return md5($fingerprint); } private static function _regenerate_id() { session_regenerate_id(); } } ?>
The config file that would go along with this class would look something like this:
class config { // Session Settings Const check_browser = true; Const check_ip_blocks = 4; Const session_salt = 'secure'; Const regenerate_id = true; Const session_timeout = 20; }
Before using this code you would need to include at the very top of your script, the following:
ini_set('session.use_cookies', 1); ini_set('session.use_only_cookies', 1); session_start();
The ini_set statements are optional and are best to include in your php.ini, but if you don’t have access to your php.ini you can use these statements for extra security.
What does it all mean???
Lets take a look at the first 2 functions; session::add_param() and session::get_param(). These are pretty much self explanitory. session::add_param() takes two parameters; $name and $value. The first being the name of the parameter and the second being the value. $session::get_param() is even easier, it only requires $name and the function simply returns the value of the given parameter. Next let’s look at session::destroy(). To truly destroy a session completely you must set the session to a blank array, unset it and then call session_destroy(). It is very important that we are able to completely and totally destroy a session as we want to ensure that no hacker can access any of our sensetive session information. The next function we’ll examine is the session::start_secure_session() static function. This function utilizes the _Fingerprint() function which is the meat of the class. The fingerprint function get’s the HTTP_USER_AGENT data which is the browser version, operating system and much more and concatenates this information with the ip address of the client, then concatenates a random string called ’salt’ (which you type in the config) and then md5 hashes it (one way encryption) so that it is secure and then adds this into the session as a parameter. So when do we use this? We use this function when we have validated that someone has logged in correctly. For instance, say a user has typed in a user name and password into a login form and we query the database to ensure that username and password really does exist in the database. Once we have validated this, we would run the session::start_secure_session(). This is the ONLY time we run this function. All other ensuing pages will run the following:
if (!session::check()) { session::destroy(); die('You have been logged out'); }
This snippet will check each page to ensure that the exact same data is coming from the browser as when the user logged in. If anything changes (operating system, browser version, ip address) then the user is logged out and the session is destroyed because this may be coming from a malicious source. Another thing to note is that the session id is regenerated each time the session is checked. This is a huge defense against session fixation and hacking as the hacker may be able to ascertain the session id once, but he will have to constantly have to get it which makes session hacking very difficult. One last feature, many times, the browser will send the referring web page that it came from. This function will check to see if this is the case and ensure that the referring page is located on the same domain, otherwise it will invalidate the session.
************* Updated 01/22/2008 ***************
Due to popular demand, I have added in a timeout. In the config section you will notice "session_timeout = 20". This specifies how many minutes the session has to live after an action has taken place. If the user has been idle for 20 minutes (in this case) the session::check() function returns false and you should invalidate the session entirely. This can decrease the possibility of session fixation.
Wrap up
There is no such thing as a perfect world. Otherwise we would not need security. We as web developers must get it through our heads that web security is important because hacking is very easy (12 year olds are hacking into military systems). If you don’t take proper steps to secure ALL of your web applications you WILL pay… it’s not maybe… you will.

December 29th, 2008 at 11:35 am
Hi,
Nice work
I have a question: Where is the constants class ?
$blocks = explode(’.', constants::Client_IP());
cya, Max
January 1st, 2009 at 8:24 pm
Hi Max,
Sorry I’ve been on vacation and havn’t had a chance to answer. The config class would be included in the main script and would include the following class:
class config
{
// Session Settings
Const check_browser = true;
Const check_ip_blocks = 4;
Const session_salt = ’secure’;
Const regenerate_id = true;
}
This will check all four IP blocks (assuming your running IP4 and not IP6). If you put 0 then it will not check IP blocks, but this is definitely a much more secure session handler than the default.
January 5th, 2009 at 2:33 pm
Hi, nice work!
the answer is not the constats class, but the config.
I think yo can use $_SERVER['REMOTE_ADDR'] instead of constants::Client_IP()
like this
$blocks = explode(’.’, $_SERVER['REMOTE_ADDR']);
January 5th, 2009 at 2:44 pm
Thanks for catching that pipu. I pulled this from my personal framework and I have another class that gets the IP address, but this is definitely what should have been included in this particular class.
January 6th, 2009 at 10:35 am
Exuse me, but I can’t understand this function:
return (isset($_SESSION['ss_fprint'])
&& $_SESSION['ss_fprint'] == self::_Fingerprint() && ($_SERVER['HTTP_REFFERER'] != ” ? (strpos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) !== false ? true : false) : true));
it means this?
if (isset($_SESSION['ss_fprint'] && $_SESSION['ss_fprint'] == self::_Fingerprint() && $_SERVER['HTTP_REFFERER'] != ”)
{
if (strpos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) !== false)
{
return true;
}
else
{
return false;
}
}
else
{
return true;
}
Many thanks
January 6th, 2009 at 2:06 pm
That section of the code checks to make sure that the session is valid. It’s broken up into a few parts. First, it makes sure that ’ss_fprint’ does exist and that ’ss_fprint’ equals the current browser ‘finger print’. ’ss_fprint’ is the session variable that was initially set and contains the session fingerprint at the time of login (so we know that was the valid user). Over time, session fixation may take place and we need to make sure that we keep checking to make sure that the browser version, operating system and ip address remain the same otherwise this may be a hacker. Next we have the shorthand if statement (in case you don’t know http://snippets.dzone.com/posts/show/76) in which we make sure that we are referring from the same domain. ‘HTTP_REFERER’ is sent from the browser and can be spoofed. Sometimes it isn’t even sent from the browser so this if statement checks to see if ‘HTTP_REFERER’ has been sent by the browser and if it has, make sure that it is the same domain (’HTTP_HOST’).
January 14th, 2009 at 6:20 am
contact management database…
Well spoken. I have to research more on this as it is really vital info….
January 19th, 2009 at 12:58 am
hacking tutorial…
I can’t believe I missed this! I’m going to have to do some more reading me thinks….
January 20th, 2009 at 3:44 pm
So the session will only fail if the end user changes web browser or ip address? Can we add a timeout to the code?
January 22nd, 2009 at 7:44 pm
Thanks Rob,
Yeah I’ve just updated this so that it now includes a timeout. If you set the timeout to 0, then there will not be a timeout, but if you specify a number greater than 0 then it will specify the minutes before the session will timeout. This now checks, IP, Client data, referring page (if it exists) and now timeout.