Skip to content

Secure Native Session

Derek Jones edited this page Jul 5, 2012 · 11 revisions

What's new?

  • 2 way for store your session information: private & public zone.
  • native php session used

Config [u]sess_expiration[/u] - 0 -> never expire (2 years), -1 -> browser session, greater 0 -> your special expire [u]sess_revalidate_id[/u] - TRUE or FALSE Warning! [u]public data[/u] has never expire setting It's works with database store enabled only!

SQL

create table ci_sessions (
session_id varchar(40) default '0' not null primary key,
ip_address varchar(16) default '0' not null,
user_agent varchar(50) not null,
last_activity integer default 0 not null,
private text default '' not null
);

Reference

function system($item) // get CI standart session information
function _sess_regenerate_allow() // you can override it for your regenerate allow
function private_data($item) // get your private item
function public_data($item) // get your public item
function set_private_data($newdata,$newval) // set your private item with arguments as CI set_userdata
function set_public_data($newdata,$newval) // set your public item with arguments as CI set_userdata
function unset_private_data($items) // delete items (array) from private data
function unset_public_data($items) // delete items (array) from public data

CI standart

function set_flashdata(...)
function flashdata(...)

How to install

  1. replace CI session class & edit your code with special Secure Native Session reference
  2. rename this name class & use with happy

P.S. not include GMT time parameter, current 'local' Discuss - http://codeigniter.com/forums/viewthread/86472/

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class CI_Session {
    
    var $CI;
    var $now;
    var $userdata = array();
    var $time_to_update = 300;
    var $gc_probability    = 5;
    var $flashdata_key = 'flash';
    var $sess_cookie = 'ci_session';
    var $session_length = 7200;
    var $session_table = 'ci_session';
    var $session_id;
    
    
    /**
        Session Constructor
    */
    function CI_Session() {
        $this->CI =& get_instance();
        log_message('debug', "Native Session Class Initialized");
        $this->sess_run();
    }
    
    /**
        get 2 years in seconds
    */
    function _get_2years_seconds() {
        return 60*60*24*365*2;
    }
    
    /**
        Run the session routines
    */
    function sess_run() {        
        // session start
        session_start();
        
        // time to update
        if (is_numeric($this->CI->config->item('sess_time_to_update')))
            $this->time_to_update = $this->CI->config->item('sess_time_to_update');
        
        // now - only 'local'
        $this->now = time();
        
        // session life
        $expiration = $this->CI->config->item('sess_expiration');
        if (is_numeric($expiration)) {
            if ($expiration > 0) // user defined
                $this->sess_length = $this->CI->config->item('sess_expiration');
            else if ($expiration < 0) // no save
                $this->sess_length = 0;
            else // save session
                $this->sess_length = $this->_get_2years_seconds();
        }
        
        // Set the cookie name
        if ($this->CI->config->item('sess_cookie_name') != FALSE)
            $this->sess_cookie = $this->CI->config->item('cookie_prefix').$this->CI->config->item('sess_cookie_name');
        
        // database always is need
        $this->session_table = $this->CI->config->item('sess_table_name');
        $this->CI->load->database();
        
        // Fetch the current session
        if (! $this->sess_read()) 
            $this->sess_create();
        else if (($this->userdata['last_activity'] + $this->time_to_update) < $this->now)
            $this->sess_update();
            
        // Fetch public zone
        $this->userdata['public'] = $this->_unserialize($this->CI->input->cookie($this->sess_cookie));
            
        // Delete expired sessions if necessary
        $this->sess_gc();
        
        // Delete 'old' flashdata (from last request)
           $this->_flashdata_sweep();
        
        // Mark all new flashdata as old (data will be deleted before next request)
           $this->_flashdata_mark();
    }
    
    /**
        Remove from the DB
    */
    function _sess_delete() {        
        $this->CI->db->where('session_id', $this->session_id);
        $this->CI->db->delete($this->session_table);
    }
    
    /**
        Fetch the current session data if it exists
    */
    function sess_read() {    
        // fetch session hash        
        $this->session_id = session_id();
        
        // read from database
        $this->CI->db->where('session_id', $this->session_id);
        $query = $this->CI->db->get($this->session_table);
        // has session id
        if ($query->num_rows()) {
            $session = $query->row_array();
            
            // Is the session current?
            if ($this->sess_length && ($session['last_activity'] + $this->sess_length) < $this->now) {
                $this->_sess_delete();
                return false;
            }
            // Does the IP Match?
            if ($this->CI->config->item('sess_match_ip') && $session['ip_address'] != $this->CI->input->ip_address())
                return false;
                
            // Does the User Agent Match?
            if ($this->CI->config->item('sess_match_useragent') && trim($session['user_agent']) != trim(substr($this->CI->input->user_agent(), 0, 50)))
                return false;
                
            // Fetch security data            
            $session['private'] = $this->_unserialize($session['private']);
                
            // Session is valid!
            $this->userdata = $session;
            unset($session);
        
            return true;
        }
        else 
            return false;
    }
    
    /**
        Set cookie
    */
    function _set_cookie($key,$value,$lifetime) {
        setcookie($key,$value,$lifetime,$this->CI->config->item('cookie_path'),$this->CI->config->item('cookie_domain'));
    }
    
    /**
        Write the session cookie
    */
    function sess_write($saved = false, $only_private = true) {    
        // set cookie
        $this->_set_cookie(session_name(),$this->session_id,$this->sess_length?$this->sess_length+time():0);
        
        // save zones
        if ($saved) {        
            // private zone
            if ($only_private)
                $this->CI->db->update($this->session_table,$this->_serialize_private(),
                    array('session_id' => $this->session_id));
            else // public zone
                $this->_set_cookie($this->sess_cookie,serialize($this->userdata['public']),$this->_get_2years_seconds()+time());
        }
    }
    
    /**
        Create a new session
    */
    function sess_create() {
        
        $this->userdata = array(
            'session_id'     => $this->session_id,
            'ip_address'     => $this->CI->input->ip_address(),
            'user_agent'     => substr($this->CI->input->user_agent(), 0, 50),
            'last_activity'    => $this->now
        );
        
        // save in the DB
        $this->CI->db->query($this->CI->db->insert_string($this->session_table, $this->userdata));
        
        // create extra fields
        $this->userdata['private'] = array();
        
        // session write
        $this->sess_write();
    }
    
    /**
        class hook for override
        fetch regenerate allow flag 
    */
    function _sess_regenerate_allow() {
        return true;
    }
    
    /**
        Update an existing sessio
    */
    function sess_update() {
        $oldssid = $this->session_id;
        
        $update['last_activity'] = $this->now;
        
        // regenerate
        if ($this->CI->config->item('sess_revalidate_id') && $this->_sess_regenerate_allow()) {
            session_regenerate_id();
            // save new session id
            $this->userdata['session_id'] = $update['session_id'] = $this->session_id = session_id();
        }
        
        // update DB
        $this->CI->db->query($this->CI->db->update_string($this->session_table, $update, array('session_id' => $oldssid)));
        
        // update data
        $this->userdata['last_activity'] = $this->now;
                
        // session write
        $this->sess_write();
    }
    
    /**
        Garbage collection
    */
    function sess_gc() {
        if ($this->sess_length == 0) return;
        // garbage go!    
        srand(time());
        if ((rand() % 100) < $this->gc_probability)    {
            $expire = $this->now - $this->sess_length;
            
            $this->CI->db->where("last_activity < {$expire}");
            $this->CI->db->delete($this->session_table);

            log_message('debug', 'Session garbage collection performed.');
        }
    }
    
    /**
        Destroy the current session
    */
    function sess_destroy() {
        $this->_sess_delete();
    
        // Unset all of the session variables.
        $_SESSION = array();

        // If it's desired to kill the session, also delete the session cookie.
        // Note: This will destroy the session, and not just the session data!
        if (isset($_COOKIE[session_name()]))
            $this->_set_cookie(session_name(),'',$this->now - 31500000);

        // Finally, destroy the session.
        session_destroy();
    }
    
    /**
        Fetch a system value
    */
    function system&#40;$item&#41; {
        return $this->userdata[$item];
    }
    
    /**
        Fetch a specific value
    */
    function _userdata($item,$type) {
        return (! isset($this->userdata[$type][$item]))?false:$this->userdata[$type][$item];
    }
    
    /**
        Fetch a public value
    */
    function public_data($item) {
        return $this->_userdata($item,'public');
    }
    
    /**
        Fetch a private value
    */
    function private_data($item) {
        return $this->_userdata($item,'private');
    }
    
    /**
        Add or change a data
    */
    function _set_userdata($type, $newdata, $newval) {
        if (is_string($newdata))
            $newdata = array($newdata => $newval);
    
        if (count($newdata) > 0) {
            foreach ($newdata as $key => $val)
                $this->userdata[$type][$key] = $val;                
            $this->sess_write(true,$type == 'private');
        }
    }
    
    /**
        Remove a data
    */
    function _unset_userdata($type,$data) {
        if (is_string($data))
            $data = array($data);
        if (count($data) > 0) {
            foreach ($data as $val)
                unset($this->userdata[$type][$val]);            
            $this->sess_write(true,$type == 'private');
        }
    }
    
    /**
        Add or change a public data
    */
    function set_public_data($newdata = array(), $newval = '') {
        $this->_set_userdata('public',$newdata,$newval);
    }
    
    /**
        Add or change a private data
    */
    function set_private_data($newdata = array(), $newval = '') {
        $this->_set_userdata('private',$newdata,$newval);
    }
    
    /**
        Remove a private data
    */
    function unset_private_data($data = array()) {
        $this->_unset_userdata('private',$data);
    }
    
    /**
        Remove a public data
    */
    function unset_public_data($data = array()) {
        $this->_unset_userdata('public',$data);
    }
    
    /**
        Add or change flashdata, only available
    */
    function set_flashdata($newdata = array(), $newval = '') {
        if (is_string($newdata))
            $newdata = array($newdata => $newval);

        if (count($newdata) > 0) {
            $flashdata = array();
            foreach ($newdata as $key => $val)
                $flashdata[$this->flashdata_key.':new:'.$key] = $val;            
            $this->set_public_data($flashdata);
        }
    }
    
    /**
        Fetch a specific flashdata item from the session array
    */
    function flashdata($key) {
        return $this->public_data($this->flashdata_key.':old:'.$key);
    }
    
    /**
        Identifies flashdata as 'old' for removal
    */
    function _flashdata_mark() {
        foreach ($this->userdata['public'] as $name => $value) {
            $parts = explode(':new:', $name);
            if (is_array($parts) && count($parts) === 2) {
                $new_name = $this->flashdata_key.':old:'.$parts[1];
                $this->userdata['public'][$new_name] = $value;
                unset($this->userdata['public'][$name]);
            }
        }
        $this->sess_write(true,false);
    }

    /**
        Removes all flashdata marked as 'old'
    */
    function _flashdata_sweep() {
        foreach ($this->userdata['public'] as $key => $value)
            if (strpos($key, ':old:'))                
                unset($this->userdata['public'][$key]);
    }
    
    /**
        unserialize string
    */
    function _unserialize($str) {
        if (is_array($data = @unserialize($this->_strip_slashes($str)))) 
            return $data;
        else
            return array();
    }
    
    /**
        Strip slashes
    */
    function _strip_slashes($vals) {
        if (is_array($vals))
             foreach ($vals as $key=>$val)
                 $vals[$key] = $this->_strip_slashes($val);
         else
             $vals = stripslashes($vals);
         return $vals;
    }
    
    /**
        Get the session serialize security
    */
    function _serialize_private() {
        return array('private' => serialize($this->userdata['private']));
    }
}
Clone this wiki locally