{"id":6304,"date":"2018-01-19T19:35:38","date_gmt":"2018-01-19T19:35:38","guid":{"rendered":"http:\/\/goofy-trucks.flywheelsites.com\/mysql-secure-session-handler\/"},"modified":"2018-01-19T19:36:21","modified_gmt":"2018-01-19T19:36:21","slug":"mysql-secure-session-handler","status":"publish","type":"post","link":"https:\/\/phpbuilder.com\/mysql-secure-session-handler\/","title":{"rendered":"Mysql Secure session Handler"},"content":{"rendered":"<div class=\"phpbuilder-content\">\n<div class=\"phpbuilder-meta\">\n<div class=\"\">By Laurent DINCLAUX<\/div>\n<div class=\"\">on February 10, 2003<\/div>\n<\/p><\/div>\n<div id=\"overflow-content\">Hi,<\/p>\n<p>As everyone knows, php build in Session management is not really secure:<\/p>\n<p><b>(1)<\/b> storing of session in \/tmp can allow other virtual hosts to access the session data<br \/><b>(2)<\/b> intercepting the SID passed by url can allow the hacker to login in with it<br \/><b>(3)<\/b> intercepting the cookie does the same<br \/>As a workaround, it will be nice to store the data in a mySQL table, disallow the use of <br \/>session.use_trans_sid and do a check on user IP address to see if it match the one used<br \/> on sesssion open.<\/p>\n<p>As I really like the way work built in PHP4 Sessions (accessing vars throw $_SESSION) I have<br \/> decided to replace the session handler to do the staff and then I could continue to use my <br \/>scripts with only replacing session_start() by a function call and by including a file.<\/p>\n<p>You can download the script from there: <a href=\"http:\/\/www.keasyphp.org\/php\/Sessions\" target=\"_blank\">http:\/\/www.keasyphp.org\/php\/Sessions<\/a><\/p>\n<p><\/p>\n<pre>&lt;?\n\/* ------------------------------------------------------------------------\n * session_mysql.inc.php\n * ------------------------------------------------------------------------\n * PHP4 MySQL Session Handler\n * Version 1.0.0\n * Last Modified: 5 Feb 2003\n *\n * Copyright (C) 2003  Laurent DINCLAUX (<a href=\"mailto:root@KeasyPHP.org\" target=\"_new\">root@KeasyPHP.org<\/a>)\n *\n *   This program is free software; you can redistribute it and\/or modify\n *   it under the terms of the GNU General Public License as published by\n *   the Free Software Foundation; either version 2 of the License, or\n *   (at your option) any later version.\n *\n *   This program is distributed in the hope that it will be useful,\n *   but WITHOUT ANY WARRANTY; without even the implied warranty of\n *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *   GNU General Public License for more details.\n *\n *   You should have received a copy of the GNU General Public License\n *   along with this program; if not, write to the Free Software\n *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n *\n * ------------------------------------------------------------------------\n * DESCRIPTION:\n * ------------------------------------------------------------------------\n * This library tells the PHP4 session handler to write to a MySQL database\n * instead of creating individual files for each session.\n * In fact it is quite secure as it can do a check against ip. This avoid\n * hacking of the cookie containing session_id by its intercept and use\n * on an other computer. It retrives firewall ip and client ip too.\n * It also has default value to override session.use_trans_sid so it disabale\n * use it as it is not secure at all.\n *\n * ------------------------------------------------------------------------\n * INSTALLATION:\n * ------------------------------------------------------------------------\n * Create a new database in MySQL called \"sessions\" like so:\n *\n *CREATE TABLE $_sess_tblPrefix_sessions (\n *   session_id varchar(32) NOT NULL default '',\n *   session_created int(11) NOT NULL default '0',\n *   session_active int(11) NOT NULL default '0',\n *   session_counter int(11) NOT NULL default '0',\n *   session_remote_address varchar(128) NOT NULL default '',\n *   session_data longtext NOT NULL\n *) TYPE=MyISAM;\n *\n * Then call sess_init($DB_host, $DB_base, $DB_user, $DB_password,$_sess_tblPrefix) to open\n * session note that this function has many other arguments that are used to\n * override php.ini session_setting they have default value so check them\n * at the end of the script and the php documentation for explanations\n * <a href=\"http:\/\/www.php.net\/manual\/en\/ref.session.php\" target=\"_blank\">http:\/\/www.php.net\/manual\/en\/ref.session.php<\/a>\n *\/\n\n$_sqlLink = '';\n$_sess_data_max = '';\n$_sess_ip_check = '';\n$_sess_tblPrefix = '';\n$_sess_property= '';\n\n\/**\n *This function retrive the more detailed info possible on client IP adress\n *\/\nfunction get_full_ip(){\n    \/\/ get client real ip\n    if ($_SERVER['HTTP_X_FORWARDED_FOR']):\n        $IP_ADDR = $_SERVER['HTTP_X_FORWARDED_FOR'] ;\n    elseif($_SERVER['HTTP_CLIENT_IP']):\n        $IP_ADDR =  $_SERVER['HTTP_CLIENT_IP'] ;\n    else:\n        $IP_ADDR = $_SERVER['REMOTE_ADDR'];\n    endif;\n\n    \/\/ get server ip and resolved it\n    $FIRE_IP_ADDR = $_SERVER['REMOTE_ADDR'];\n    $ip_resolved = gethostbyaddr($FIRE_IP_ADDR);\n\n    \/\/ builds server ip infos string\n    if ($FIRE_IP_ADDR != $ip_resolved &amp;&amp; $ip_resolved):\n        $FIRE_IP_LITT = $FIRE_IP_ADDR.\" - \". $ip_resolved;\n    else:\n        $FIRE_IP_LITT = $FIRE_IP_ADDR;\n    endif;\n\n    \/\/ builds client ip full infos string\n    if ($IP_ADDR != $FIRE_IP_ADDR  ):\n        $FULL_IP_INFOS = \"$IP_ADDR | $FIRE_IP_LITT\" ;\n    else:\n        $FULL_IP_INFOS = $FIRE_IP_LITT ;\n    endif;\n    return $FULL_IP_INFOS;\n\n}\n\n\/**\n *Called by session_start()\n *only opens a Mysql connection\n *\/\nfunction sess_open($save_path, $_session_name) {\n    global $DB_host, $DB_base, $DB_user, $DB_password, $_sqlLink;\n\n    if (! $_sqlLink =@ mysql_connect($DB_host, $DB_user, $DB_password)) {\n        trigger_error('sess_open(): Failed to connect to MySql - '. mysql_error($_sqlLink),E_USER_ERROR);\n        return false;\n    }\n    if (! @mysql_select_db($DB_base, $_sqlLink)) {\n        trigger_error(\"sess_open(): Unable to select database $DB_base - \". mysql_error($_sqlLink),E_USER_ERROR);\n        return false;\n    }\n    return true;\n}\n\nfunction sess_close() {\n    return true;\n}\n\n\/**\n *Reads session data in mySql\n *also do an ip check\n *\/\nfunction sess_read($session_id) {\n    global $_sqlLink, $_sess_property, $_sess_tblPrefix, $_sess_ip_check;\n\n    \/\/session_id check\n    if (strlen($session_id) != 32) {\n        trigger_error(\"sess_read(): Invalid SessionID = \" . $session_id,E_USER_ERROR);\n        return '';\n    }\n\n    $session_id = addslashes($session_id);\n    $result = mysql_query(\"SELECT * FROM {$_sess_tblPrefix}_sessions WHERE session_id = '$session_id'\",$_sqlLink);\n    if (@mysql_num_rows($result) == 1) {\n        $_sess_property= mysql_fetch_assoc($result);\n        return $_sess_property['session_data'];\n        \/\/ ip check\n        if(!$_sess_ip_check and $_sess_property){\n            return $_sess_property['session_data'];\n        }\n        else if($_sess_property and $_sess_property['session_remote_address']==get_full_ip() and $_sess_ip_check){\n            return $_sess_property['session_data'];\n        }\n        else{\n           echo \"&lt;DIV align=center&gt;&lt;font face=verdana size=1&gt;<b>Session Error<\/b><br\/>Your IP address has changed...<br\/>'\".get_full_ip().\"'<br\/>'{$_sess_property['session_remote_address']}'<br\/>Remember: hacking is not good for your health !&lt;\/font&gt;&lt;\/div&gt;\";\n           session_destroy();\n           exit;\n           return '';\n        }\n\n    }\n    elseif (mysql_errno($_sqlLink)) {\n        trigger_error('sess_read(): Failed to read sessions - '. mysql_error($_sqlLink),E_USER_ERROR);\n        return '';\n    }\n    else {\n        $_sess_property = null; \/\/ For session_write()\n        return '';\n    }\n}\n\/**\n *Write session data in mySql\n *also do an ip check\n *\/\nfunction sess_write($session_id, $session_data) {\n    global $_sqlLink, $_sess_property, $_sess_tblPrefix, $_sess_data_max, $_sess_ip_check;\n\n    \/\/session_id check\n    if (strlen($session_id) != 32) {\n        trigger_error('sess_write(): Invalid Session ID = '.$session_id,E_USER_ERROR);\n        return false;\n    }\n    \/\/session data max size check\n    if ($_sess_data_max &gt; 0 and strlen($session_data) &gt; intval($_sess_data_max)) {\n        trigger_error('sess_write(): Session data too large. '. $session_id, E_USER_ERROR);\n    }\n    \/\/ ip check\n    if($_sess_property and $_sess_property['session_remote_address']!=get_full_ip() and $_sess_ip_check){\n        echo get_full_ip().\"&lt;DIV align=center&gt;&lt;font face=verdana size=1&gt;<b>Session Error<\/b><br\/>Your IP address has changed...<br\/>'\".get_full_ip().\"'<br\/>'{$_sess_property['session_remote_address']}'<br\/>Remember: hacking is not good for your health !&lt;\/font&gt;&lt;\/div&gt;\";\n        session_destroy();\n        exit;\n        return '';\n    }\n\n\n    \/\/ escape session_data to be insert in Mysql\n    if(version_compare(phpversion(), \"4.3.0\", \"&lt;\")){\n        $_sess_data = mysql_escape_string($session_data);\n    }\n    else{\n        $_sess_data = mysql_real_escape_string($session_data);\n    }\n    \/\/UPDATE\/INSERT data\n    if ($_sess_property) {\n        $query = \"UPDATE {$_sess_tblPrefix}_sessions SET session_active = \". time() .\", session_counter = \". ++$_sess_property['session_counter'] .\", session_data = '$_sess_data' WHERE session_id = '$session_id';\";\n    }\n    else {\n        $query = \"INSERT INTO {$_sess_tblPrefix}_sessions (session_id, session_created, session_active, session_remote_address, session_data) VALUES ('$session_id', \". time() .\", \". time() .\", '\". get_full_ip() .\"', '$_sess_data');\";\n    }\n    mysql_query($query, $_sqlLink);\n\n    if (mysql_errno($_sqlLink)) {\n        trigger_error('sess_write(): Failed to INSERT\/UPDATE session. '.mysql_error($_sqlLink). \"<br\/> Query: \".$query,E_USER_ERROR);\n    }\n    return true;\n}\n\/**\n *detroys the session\n *\/\nfunction sess_destroy($session_id) {\n    global $_sqlLink, $_sess_tblPrefix;\n\n    mysql_query(\"DELETE FROM {$_sess_tblPrefix}_sessions WHERE session_id = '\". addslashes($session_id). \"'\",$_sqlLink);\n    if (mysql_errno($_sqlLink)) {\n        trigger_error('sess_destory(): Failed to DELETE session. '.mysql_error($_sqlLink),E_USER_ERROR);\n        return false;\n    }\n    else {\n        return true;\n    }\n}\n\/**\n *delete old sessions\n *\/\nfunction sess_gc($_sess_gc_maxlifetime=20) {\n   \/**\n    *you may want to uncomment this to test if garbage\n    *collection get called properly\n    *\/\n\n    \/* $fp =fopen('gc.txt',ab);\n    fwrite ($fp,\"gcn\".\"DELETE FROM {$_sess_tblPrefix}_sessions WHERE session_active &lt; \". (time() - $_sess_gc_maxlifetime));\n    fclose($fp);\n    *\/\n\n    global $_sqlLink, $_sess_tblPrefix;\n    mysql_query(\"DELETE FROM {$_sess_tblPrefix}_sessions WHERE session_active &lt; \". (time() - $_sess_gc_maxlifetime));\n    mysql_query(\"OPTIMIZE TABLE `{$_sess_tblPrefix}_sessions` \",$_sqlLink);\n    if (mysql_errno($_sqlLink)) {\n        trigger_error('sess_gc(): Failed to DELETE old sessions.'. mysql_error($_sqlLink),E_USER_WARNING);\n        return false;\n    }\n    else {\n        return true;\n    }\n}\n\n\/**\n *initaializing session function\n *call that to open session\n *\/\nfunction sess_init($DB_host, $DB_base, $DB_user, $DB_password, $session_tblPrefix, \/\/vars to connect to mysql\n                   $session_ip_check = true, \/\/ do an ip check ?\n                   $session_data_max = 0, \/\/ max lenght of session data (0 for unlimited)\n                   $session_name=\"PHPSESSID\",\n                   $session_serialize_handler = 'php',\n                   $session_gc_probability = 50,\n                   $session_gc_maxlifetime = 1440,\n                   $session_referer_check  ='',\n                   $session_entropy_file = '',\n                   $session_entropy_length = 0,\n                   $session_use_cookies = 1,\n                   $session_use_only_cookies = 1, \/\/ only available from php 4.3.0\n                   $session_cookie_lifetime = 0,\n                   $session_cookie_secure = false, \/\/ automaticly set if you use https connection but you can force it to true\n                   $session_cookie_path = '\/',\n                   $session_cookie_domain = '',\n                   $session_cache_limiter = 'nocache', \/\/ none, nocache, private, private_no_expire, public\n                   $session_cache_expire = 180,\n                   $session_use_trans_sid = 0,\n                   $sesssion_url_rewriter_tags = 'a=href,area=href,frame=src,input=src,form=fakeentry')\n{\n    global $DB_host, $DB_base, $DB_user, $DB_password, $_sess_tblPrefix, $_sess_ip_check, $_sess_data_max, $_sess_gc_maxlifetime;\n\n    $_sess_data_max = $session_data_max;\n    $_sess_ip_check = $session_ip_check;\n    $_sess_tblPrefix = $session_tblPrefix;\n    \/\/ have to or the staff won't work\n    ini_set ( \"session.save_handler\", \"user\" );\n\n    \/\/ set the cookie secure if HTTPS is on\n    if($_SERVER[\"HTTPS\"] == \"on\" or $session_cookie_secure) $session_cookie_secure = true;\n\n    session_name($session_name);\n\n    ini_set ( \"session.serialize_handler\", $session_serialize_handler );\n    ini_set ( \"session.gc_probability\", $session_gc_probability );\n    ini_set ( \"session.gc_maxlifetime\", $session_gc_maxlifetime );\n    ini_set ( \"session.referer_check\", $session_referer_check );\n    ini_set ( \"session.entropy_file\", $session_entropy_file);\n    ini_set ( \"session.entropy_length\", $session_entropy_length );\n    ini_set ( \"session.use_cookies\", $session_use_cookies );\n    if(version_compare(phpversion(), \"4.3.0\", \"&gt;=\")) ini_set ( \"session.use_only_cookies\", $session_use_only_cookies );\n\n    session_set_cookie_params ( $session_cookie_lifetime,$session_cookie_path,$session_cookie_domain,$session_cookie_secure);\n    session_cache_expire ($session_cache_expire);\n    session_cache_limiter($session_cache_limiter);\n\n    ini_set ( \"session.use_trans_sid\", $session_use_trans_sid );\n    ini_set ( \"session.url_rewriter.tags \", $sesssion_url_rewriter_tags );\n\n    $_sess_gc_maxlifetime = ini_get(\"session.gc_maxlifetime\");\n\n    session_start();\n}\n\n\nsession_set_save_handler(\n        \"sess_open\",\n        \"sess_close\",\n        \"sess_read\",\n        \"sess_write\",\n        \"sess_destroy\",\n        \"sess_gc\");\n\n?&gt;<\/pre>\n<\/div><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Hi,As everyone knows, php build in Session management is not really secure:(1) storing of session in \/tmp can allow other<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-6304","post","type-post","status-publish","format-standard","hentry","category-news"],"_links":{"self":[{"href":"https:\/\/phpbuilder.com\/wp-json\/wp\/v2\/posts\/6304","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/phpbuilder.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/phpbuilder.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/phpbuilder.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/phpbuilder.com\/wp-json\/wp\/v2\/comments?post=6304"}],"version-history":[{"count":1,"href":"https:\/\/phpbuilder.com\/wp-json\/wp\/v2\/posts\/6304\/revisions"}],"predecessor-version":[{"id":7129,"href":"https:\/\/phpbuilder.com\/wp-json\/wp\/v2\/posts\/6304\/revisions\/7129"}],"wp:attachment":[{"href":"https:\/\/phpbuilder.com\/wp-json\/wp\/v2\/media?parent=6304"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/phpbuilder.com\/wp-json\/wp\/v2\/categories?post=6304"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/phpbuilder.com\/wp-json\/wp\/v2\/tags?post=6304"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}