Skip to content

Secure Native Session

World Wide Web Server edited this page Jul 4, 2012 · 11 revisions

What's new?

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

[b]Config[/b] [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 [i]Warning![/i] [u]public data[/u] has never expire setting It's works with database store enabled only!

[b]SQL[/b] [code] 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 ); [/code]

[b]Reference[/b] [code] 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 [/code]

[b]CI standart[/b] [code] function set_flashdata(...) function flashdata(...) [/code]

[b]How to install[/b]

  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/

[code] <?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']));
}

}[/code]

Clone this wiki locally