forked from bcit-ci/CodeIgniter
-
Notifications
You must be signed in to change notification settings - Fork 4
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
- replace CI session class & edit your code with special Secure Native Session reference
- 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($item) {
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']));
}
}