Index: trunk/core/kernel/session/session.php =================================================================== diff -u -r2491 -r2669 --- trunk/core/kernel/session/session.php (.../session.php) (revision 2491) +++ trunk/core/kernel/session/session.php (.../session.php) (revision 2669) @@ -5,33 +5,33 @@ The session works the following way: 1. When a visitor loads a page from the site the script checks if cookies_on varibale has been passed to it as a cookie. -2. If it has been passed, the script tries to get Session ID (SID) from the request: +2. If it has been passed, the script tries to get Session ID (SID) from the request: 3. Depending on session mode the script is getting SID differently. The following modes are available: - - smAUTO - Automatic mode: if cookies are on at the client side, the script relays only on cookies and - ignore all other methods of passing SID. - If cookies are off at the client side, the script relays on SID passed through query string - and referal passed by the client. THIS METHOD IS NOT 100% SECURE, as long as attacker may + + smAUTO - Automatic mode: if cookies are on at the client side, the script relays only on cookies and + ignore all other methods of passing SID. + If cookies are off at the client side, the script relays on SID passed through query string + and referal passed by the client. THIS METHOD IS NOT 100% SECURE, as long as attacker may get SID and substitude referal to gain access to user' session. One of the faults of this method - is that the session is only created when the visitor clicks the first link on the site, so + is that the session is only created when the visitor clicks the first link on the site, so there is NO session at the first load of the page. (Actually there is a session, but it gets lost after the first click because we do not use SID in query string while we are not sure if we need it) - + smCOOKIES_ONLY - Cookies only: in this mode the script relays solely on cookies passed from the browser - and ignores all other methods. In this mode there is no way to use sessions for clients - without cookies support or cookies support disabled. The cookies are stored with the + and ignores all other methods. In this mode there is no way to use sessions for clients + without cookies support or cookies support disabled. The cookies are stored with the full domain name and path to base-directory of script installation. - - smGET_ONLY - GET only: the script will not set any cookies and will use only SID passed in - query string using GET, it will also check referal. The script will set SID at the + + smGET_ONLY - GET only: the script will not set any cookies and will use only SID passed in + query string using GET, it will also check referal. The script will set SID at the first load of the page - - smCOOKIES_AND_GET - Combined mode: the script will use both cookies and GET right from the start. If client has + + smCOOKIES_AND_GET - Combined mode: the script will use both cookies and GET right from the start. If client has cookies enabled, the script will check SID stored in cookie and passed in query string, and will - use this SID only if both cookie and query string matches. However if cookies are disabled on the + use this SID only if both cookie and query string matches. However if cookies are disabled on the client side, the script will work the same way as in GET_ONLY mode. - + 4. After the script has the SID it tries to load it from the Storage (default is database) 5. If such SID is found in the database, the script checks its expiration time. If session is not expired, it updates its expiration, and resend the cookie (if applicable to session mode) @@ -59,17 +59,17 @@ //Implements session storage in the database class SessionStorage extends kDBBase { - + var $Expiration; var $SessionTimeout=0; - + var $OriginalData=Array(); - + var $TimestampField; var $SessionDataTable; var $DataValueField; var $DataVarField; - + function Init($prefix,$special) { parent::Init($prefix,$special); @@ -80,60 +80,60 @@ $this->DataValueField = 'value'; $this->DataVarField = 'var'; } - + function setSessionTimeout($new_timeout) { $this->SessionTimeout = $new_timeout; } - + function StoreSession(&$session) { $query = ' INSERT INTO '.$this->TableName.' ('.$this->IDField.', '.$this->TimestampField.')'. ' VALUES ('.$this->Conn->qstr($session->SID).', '.$session->Expiration.')'; $this->Conn->Query($query); } - + function DeleteSession(&$session) { $query = ' DELETE FROM '.$this->TableName.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->SID); $this->Conn->Query($query); - + $query = ' DELETE FROM '.$this->SessionDataTable.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->SID); $this->Conn->Query($query); - + $this->OriginalData = Array(); } - + function UpdateSession(&$session, $timeout=0) { $query = ' UPDATE '.$this->TableName.' SET '.$this->TimestampField.' = '.$session->Expiration.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->SID); $this->Conn->Query($query); } - + function LocateSession($sid) { $query = ' SELECT '.$this->TimestampField.' FROM '.$this->TableName.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($sid); $result = $this->Conn->GetOne($query); - + if($result===false) return false; - + $this->Expiration = $result; return true; } - + function GetExpiration() { return $this->Expiration; } - + function LoadData(&$session) { $query = 'SELECT '.$this->DataValueField.','.$this->DataVarField.' FROM '.$this->SessionDataTable.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->SID); - + $this->OriginalData = $this->Conn->GetCol($query, $this->DataVarField); return $this->OriginalData; } - + /** * Enter description here... * @@ -144,19 +144,19 @@ { return $this->Conn->GetOne('SELECT '.$var_name.' FROM '.$this->TableName.' WHERE `'.$this->IDField.'` = '.$this->Conn->qstr($session->GetID()) ); } - + function SetField(&$session, $var_name, $value) { return $this->Conn->Query('UPDATE '.$this->TableName.' SET '.$var_name.' = '.$this->Conn->qstr($value).' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->GetID()) ); } - + function SaveData(&$session) { if(!$session->SID) return false; // can't save without sid - + $ses_data = $session->Data->GetParams(); - - $replace = ''; + + $replace = ''; foreach ($ses_data as $key => $value) { if ( isset($this->OriginalData[$key]) && $this->OriginalData[$key] == $value) @@ -177,21 +177,21 @@ $this->Conn->Query($query); } } - + function RemoveFromData(&$session, $var) { $query = 'DELETE FROM '.$this->SessionDataTable.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->SID). ' AND '.$this->DataVarField.' = '.$this->Conn->qstr($var); $this->Conn->Query($query); unset($this->OriginalData[$var]); } - + function GetExpiredSIDs() { $query = ' SELECT '.$this->IDField.' FROM '.$this->TableName.' WHERE '.$this->TimestampField.' > '.time(); return $this->Conn->GetCol($query); } - + function DeleteExpired() { $expired_sids = $this->GetExpiredSIDs(); @@ -200,7 +200,7 @@ $where_clause=' WHERE '.$this->IDField.' IN ("'.implode('","',$expired_sids).'")'; $sql = 'DELETE FROM '.$this->SessionDataTable.$where_clause; $this->Conn->Query($sql); - + $sql = 'DELETE FROM '.$this->TableName.$where_clause; $this->Conn->Query($sql); } @@ -215,74 +215,75 @@ class Session extends kBase { var $Checkers; - + var $Mode; var $GETName = 'sid'; - + var $CookiesEnabled = true; var $CookieName = 'sid'; var $CookieDomain; var $CookiePath; var $CookieSecure = 0; - + var $SessionTimeout = 3600; var $Expiration; - + var $SID; - + var $Storage; - + var $CachedNeedQueryString = null; - + var $Data; - + function Session($mode=smAUTO) { parent::kBase(); $this->SetMode($mode); } - + function SetMode($mode) { $this->Mode = $mode; + $this->CachedNeedQueryString = null; } - + function SetCookiePath($path) { $this->CookiePath = $path; } - + function SetCookieDomain($domain) { $this->CookieDomain = $domain; } - + function SetGETName($get_name) { $this->GETName = $get_name; } - + function SetCookieName($cookie_name) { $this->CookieName = $cookie_name; } - + function InitStorage() { $this->Storage =& $this->Application->recallObject('SessionStorage'); $this->Storage->setSessionTimeout($this->SessionTimeout); } - + function Init($prefix,$special) { parent::Init($prefix,$special); - + $this->CheckIfCookiesAreOn(); $this->Checkers = Array(); $this->InitStorage(); $this->Data =& new Params(); - + $tmp_sid = $this->GetPassedSIDValue(); if( !(defined('IS_INSTALL') && IS_INSTALL) ) @@ -294,7 +295,7 @@ $this->Application->HandleEvent($event, 'u:OnSessionExpire'); } } - + if ($this->Check()) { $this->SID = $this->GetPassedSIDValue(); $this->Refresh(); @@ -304,28 +305,31 @@ $this->SetSession(); } } - - function CheckReferer() + + function CheckReferer($for_cookies=0) { + // if it is a redirect from http to https + if (!$for_cookies && PROTOCOL == 'https://' && preg_match('#http:\/\/#', $_SERVER['HTTP_REFERER'])) return true; + $path = preg_replace("/admin$/", '', $this->CookiePath); // removing /admin for compatability with in-portal (in-link/admin/add_link.php) $reg = '#^'.preg_quote(PROTOCOL.$this->CookieDomain.$path).'#'; return preg_match($reg, $_SERVER['HTTP_REFERER']) || (defined('IS_POPUP') && IS_POPUP); } - + function CheckIfCookiesAreOn() { if ($this->Mode == smGET_ONLY || (defined('INPORTAL_ENV')&&INPORTAL_ENV && defined('ADMIN')&&ADMIN && !$this->Application->GetVar('front')) ) - { + { //we don't need to bother checking if we would not use it $this->CookiesEnabled = false; return; } $http_query =& $this->Application->recallObject('HTTPQuery'); $cookies_on = isset($http_query->Cookie['cookies_on']); // not good here - - if (!$cookies_on) { + + if (!$cookies_on) { //If referer is our server, but we don't have our cookies_on, it's definetly off - if ($this->CheckReferer() && !$this->Application->GetVar('admin')) { + if ($this->CheckReferer(1) && !$this->Application->GetVar('admin')) { $this->CookiesEnabled = false; } else { @@ -341,72 +345,72 @@ ); } } - else + else $this->CookiesEnabled = true; return $this->CookiesEnabled; } - + function Check() { - // we should check referer if cookies are disabled, and in combined mode + // we should check referer if cookies are disabled, and in combined mode // auto mode would detect cookies, get only mode would turn it off - so we would get here // and we don't care about referal in cookies only mode if ( $this->Mode != smCOOKIES_ONLY && (!$this->CookiesEnabled || $this->Mode == smCOOKIES_AND_GET) ) { - if (!$this->CheckReferer()) + if (!$this->CheckReferer()) return false; } - + $sid = $this->GetPassedSIDValue(); - + if (empty($sid)) return false; - + //try to load session by sid, if everything is fine - $result = $this->LoadSession($sid); - + $result = $this->LoadSession($sid); + return $result; } - + function LoadSession($sid) { if( $this->Storage->LocateSession($sid) ) { //if we have session with such SID - get its expiration $this->Expiration = $this->Storage->GetExpiration(); - + //If session has expired - if ($this->Expiration < time()) return false; - + if ($this->Expiration < time()) return false; + //Otherwise it's ok return true; } else //fake or deleted due to expiration SID return false; } - + function GetPassedSIDValue($use_cache = 1) { if (!empty($this->CachedSID) && $use_cache) return $this->CachedSID; $http_query =& $this->Application->recallObject('HTTPQuery'); $get_sid = getArrayValue($http_query->Get, $this->GETName); - + if ($this->Application->GetVar('admin') == 1 && $get_sid) { $sid = $get_sid; } else { switch ($this->Mode) { case smAUTO: - //Cookies has the priority - we ignore everything else + //Cookies has the priority - we ignore everything else $sid=$this->CookiesEnabled ? getArrayValue($http_query->Cookie,$this->CookieName) : $get_sid; break; case smCOOKIES_ONLY: - $sid = $http_query->Cookie[$this->CookieName]; + $sid = $http_query->Cookie[$this->CookieName]; break; case smGET_ONLY: $sid = $get_sid; break; case smCOOKIES_AND_GET: - $cookie_sid = $http_query->Cookie[$this->CookieName]; + $cookie_sid = $http_query->Cookie[$this->CookieName]; //both sids should match if cookies are enabled - if (!$this->CookiesEnabled || ($cookie_sid == $get_sid)) + if (!$this->CookiesEnabled || ($cookie_sid == $get_sid)) { $sid = $get_sid; //we use get here just in case cookies are disabled } @@ -417,15 +421,15 @@ break; } } - + if ($this->Application->GetVar('front')) { $this->CookiesEnabled = false; } - + $this->CachedSID = $sid; return $this->CachedSID; } - + /** * Returns session id * @@ -436,7 +440,7 @@ { return $this->SID; } - + /** * Generates new session id * @@ -445,8 +449,8 @@ */ function GenerateSID() { - list($usec, $sec) = explode(" ",microtime()); - + list($usec, $sec) = explode(" ",microtime()); + $sid_part_1 = substr($usec, 4, 4); $sid_part_2 = mt_rand(1,9); $sid_part_3 = substr($sec, 6, 4); @@ -459,7 +463,7 @@ $this->setSID($sid_part_1.$sid_part_2.$sid_part_3); return $this->SID; } - + /** * Set's new session id * @@ -471,15 +475,15 @@ $this->SID=$new_sid; $this->Application->SetVar($this->GETName,$new_sid); } - + function SetSession() { $this->GenerateSID(); $this->Expiration = time() + $this->SessionTimeout; switch ($this->Mode) { case smAUTO: if ($this->CookiesEnabled) { - $this->SetSessionCookie(); + $this->SetSessionCookie(); } break; case smGET_ONLY: @@ -492,7 +496,7 @@ $this->Storage->StoreSession($this); $this->Application->HandleEvent( new kEvent('visits:OnRegisterVisit') ); } - + function SetSessionCookie() { setcookie( @@ -504,7 +508,7 @@ $this->CookieSecure ); } - + /** * Refreshes session expiration time * @@ -515,7 +519,7 @@ if ($this->CookiesEnabled) $this->SetSessionCookie(); //we need to refresh the cookie $this->Storage->UpdateSession($this); } - + function Destroy() { $this->Storage->DeleteSession($this); @@ -524,7 +528,7 @@ if ($this->CookiesEnabled) $this->SetSessionCookie(); //will remove the cookie due to value (sid) is empty $this->SetSession(); //will create a new session } - + function NeedQueryString($use_cache = 1) { if ($this->CachedNeedQueryString != null && $use_cache) return $this->CachedNeedQueryString; @@ -543,16 +547,16 @@ case smCOOKIES_AND_GET: $res = 1; break; - } + } $this->CachedNeedQueryString = $res; return $res; } - + function LoadData() { $this->Data->AddParams($this->Storage->LoadData($this)); } - + function PrintSession($comment='') { if( $this->Application->isDebugMode() && dbg_ConstOn('DBG_SHOW_SESSIONDATA') ) @@ -569,14 +573,14 @@ } } $debugger->dumpVars($session_data); - + // to insert after HTTPQuery if it's visible $new_row = dbg_ConstOn('DBG_SHOW_HTTPQUERY') ? 4 : 2; - + //$debugger->moveAfterRow($new_row,2); - } + } } - + function SaveData() { if( $this->Application->GetVar('t') != 'help' ) @@ -587,12 +591,12 @@ $this->PrintSession('after save'); $this->Storage->SaveData($this); } - + function StoreVar($name, $value) { $this->Data->Set($name, $value); } - + function StoreVarDefault($name, $value) { $tmp = $this->RecallVar($name); @@ -601,29 +605,29 @@ $this->StoreVar($name, $value); } } - + function RecallVar($name,$default=false) { $ret = $this->Data->Get($name); return ($ret===false) ? $default : $ret; } - + function RemoveVar($name) { $this->Storage->RemoveFromData($this, $name); $this->Data->Remove($name); } - + function GetField($var_name) { return $this->Storage->GetField($this, $var_name); } - + function SetField($var_name, $value) { $this->Storage->SetField($this, $var_name, $value); } - + /** * Deletes expired sessions * @@ -634,7 +638,7 @@ { return $this->Storage->DeleteExpired(); } - + } ?> \ No newline at end of file