Member Avatar for iamthwee

I'm beginning to wonder... I hope someone can clarify my doubts.

First let me explain. So I've been using codeigniter's session library without much thought. I assumed the code was just some wrapper for PHP native sessions. So I assumed it was stored server side so encryption and all that jazz I needn't worry about.

Then I began to do some digging and realised to my horror it is stored client side in the cookie. Codeigniter uses it's own session, it is not a PHP session at all.

Now I know you can't use codeigniter's session without setting an encryption key. So I assumed, great it must be encrypted by default- phew.

But get this... if you don't change the sessions config file to.

$config['sess_encrypt_cookie'] = TRUE; <--True

isn't it storing all your codeigniter sessions in plain text!!! So if I set a session isloggedin('yes') couldn't the user edit the cookie in plain text if I didn't set the sess_encrypt_cookie to TRUE. And more importantly isn't this the default codeigniter install setting?! It is set to false, so what's the point of forcing the user's to enter an encryption key before using codeigniter's sessions!?

Second I've been reading about countless issues with their session library logging people out when a site heavily relies on ajax. I'm assuming this might be one of the reasons DW had issues with users getting logged out from time to time.

I'm starting to think this is pretty lousy.

Well the session class at least. I don't know have I got the wrong end of the stick? Veedeo /Cereal/ Dani?

Yes, you are correct a clever user can edit their session. Just be very careful and always make sure to manually itimized the session you ONLY! need for the user to move around the site.

I don't use this method as strongly suggested by many blog tutorials

$this->session->set_userdata();

What I normally do to my application is to manually assign things that I need so that I can use it to query any additional data about the user. For example, if user A is logged_in and he has been granted the Privilege of 300 which is equivalent to regular memeber in the hierarchy of users.

my session assignment would be pretty conservative. I would only assigned three to four things in there at the most.

$sess_data = array(
               'username'  => 'user_name',
               'user_id' => 'user_id',
               'time_logged_in' => time(), //always affix and suffix something and make sure to add delimiters anywhere for you to explode.
               'logged_in' => TRUE
           );

$this->session->set_userdata($sess_data);

we can set the number of minutes in the script to re-verify the user or else logged him out.

$session_expiry = 3600;

if(time() - $this->session->userdata('time_logged_in') >= $session_expiry){

    ## time to terminate the session for this user
    $this->session->sess_destroy();
    redirect('mainpage', 'refresh');

}

Controlling page access based on privilege rule. There are some sites where the contents shown based on the user privilege. For example, all members with priv 300 can only view banana pictures, priv 400 are allowed to view pears and apples, and priv 500 can view all the fruits available in the site.

So, if the basis of our decision are on this session cofigurations

$sess_data = array(
               'username'  => 'user_name_A',
               'user_id' => 'user_id',
               'priv' => 300,
               'time_logged_in' => time(), //always affix and suffix something and make sure add underscore anywhere for you to explode.
               'logged_in' => TRUE
           );

$this->session->set_userdata($sess_data);

User A can only view the banana but pictures. However, if User A is clever enough, he can literally change his priv to 500 and therefore, he will be enjoying his new found privs viewing all the furits.

How can we prevent the above scenario? Perhaps calling the United Nations will not do us any good that is for sure :).

What we can do is to verify the user's priv, before delivering the content available to him/her.

if($this->session->userdata->('loggeg_in')){
    ## the user validates to be logged in based the session stored
    ## we moved on to the next. we send this information to the model for verification

    $res = $this->User_model->check_priv($user_id,$user_name);

    foreach($res as $re){

                $real_priv = $re->priv;

                }
    ## deliver the fruit entitlement based on the object $re->priv            
    }

Luckily, we don't really have to do all of the above. CI session class serves two purpose one is for light security to no security required. Second, for the tight security requirements which requires a database for saving the session.

Just save the session in the database for session ID verification and your worries should be gone.

I forgot, when you distribute your application, make sure that your installation database table for members must have a predefined auto-increment value to prevent clever users from guessing the member's user id.

So for example, we packaged our application for distribution of a beta release.

Normally, installer will have to parse the table from the default sql file. Below is a make up users table..

CREATE TABLE IF NOT EXISTS `users` (
  `id_user` int(11) NOT NULL AUTO_INCREMENT,

  PRIMARY KEY (`id_user`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 ;

to prevent incrementing from 1 of which in most cases are assigned to the admin, we can change the distribution sql to something like this for all tables with auto increment

CREATE TABLE IF NOT EXISTS `users` (
  `id_user` int(11) NOT NULL AUTO_INCREMENT,

  PRIMARY KEY (`id_user`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1549;

the first registered user will be given the next incremented value from 1549 and not 1 which is almost a give away to anyone who knows how to spoof sessions.

Member Avatar for iamthwee

hang on... If I set cookie_sess encrypt to be true isn't this secure so I can store user privileges in sessions? If not... my system is totally vulnerable.

Be sure you have mcrypt() installed per the other thread you started about the vulnerability.

http://www.daniweb.com/community-center/daniweb-community-feedback/threads/480877/codeigniter-session-cookie-vulnerability

Otherwise, yeah, from what I understand, session information is stored encrypted in a cookie (as long as you have the encryption option enabled) and replicated in the database (as long as you have the database option enabled).

Also, if you run this

<?php 

phpinfo(); 

?>

you will be able to check for whatever php extension you have installed on your server.

If you go down about 3/4 of the page, you will see the session directives. The latest Registered serializer handlers is php php_binary wddx.

Most PHP configuration will always have files as the default value for the session.save_handler directive. So, if you set the CI to save it to the database, then this directive will be overriden.

The session.save path is always defaulted to tmp directory. However, if you want to change this to some encrypted name, you are always welcome to do so, as long as you change the value on your php.ini file.

If you are using xampp or equivalent, you can literally view the sessions generated by PHP

For example, Symfony2 will generate a session text file inside the tmp directory similar to this.

logged|
    b:0;id_user|
    i:0;username|
    s:0:"";privs|
    i:0;email|
    s:0:"";_symfony2|
    a:3:{
    s:10:"attributes";a:0:{}s:7:
    "flashes";a:0:{}s:6:"locale";s:2:"en";}

does it look familiar? those are the same as the example above, but I would never post anything on how CI handles its session transactions.

Looking at the code above, it appears that this session belongs to the site admin of a Symfony2 website. The privs for the admin is 7, the user id of the admin is 6.

Can we fixiate this session ? Sure, middle school kids can probably figure this out in no time.

Member Avatar for iamthwee

I don't have the database option enabled, for sessions. Do I need to?

I'm really confused. I thought sessions should be inaccessible by the client. So this is really worrying me that codeigniter stores this in a cookie.

  1. We're assuming the user has mcrypt() installed
  2. We're assuming the user has set cookie_encyrpt() to true
  3. We're assuming they're storing sessions in the dabase or NOT?

Still confused about that one. Seriously, there should be a massive disclaimer about using CI sessions! It's ridiculous.

If you use:

$config['sess_encrypt_cookie']  = TRUE;
$config['sess_use_database']    = TRUE;

And create a session table:

CREATE TABLE IF NOT EXISTS  `ci_sessions` (
    session_id varchar(40) DEFAULT '0' NOT NULL,
    ip_address varchar(45) DEFAULT '0' NOT NULL,
    user_agent varchar(120) NOT NULL,
    last_activity int(10) unsigned DEFAULT 0 NOT NULL,
    user_data text NOT NULL,
    PRIMARY KEY (session_id),
    KEY `last_activity_idx` (`last_activity`)
);

The session cookie will store only this information:

  • session_id
  • ip_address
  • user_agent
  • last_activity

And the cookie will be encrypted with mcrypt, up to version 2.1.4 if mcrypt was not available then they used the _xor_encode() method:

function encode($string, $key = '')
{
    $key = $this->get_key($key);

    if ($this->_mcrypt_exists === TRUE)
    {
        $enc = $this->mcrypt_encode($string, $key);
    }
    else
    {
        $enc = $this->_xor_encode($string, $key);
    }

    return base64_encode($enc);
}

In version 2.2.0 the _xor_encode() method was removed and the encode method now works like this:

function encode($string, $key = '')
{
    $key = $this->get_key($key);
    $enc = $this->mcrypt_encode($string, $key);

    return base64_encode($enc);
}

and in constructor they check if mcrypt is available:

public function __construct()
{
    $this->CI =& get_instance();
    $this->_mcrypt_exists = ( ! function_exists('mcrypt_encrypt')) ? FALSE : TRUE;

    if ($this->_mcrypt_exists === FALSE)
    {
        show_error('The Encrypt library requires the Mcrypt extension.');
    }

    log_message('debug', "Encrypt Class Initialized");
}

If not the Encrypt class won't start.

Ref:

Custom data, instead, will be saved in ci_sessions.user_data. So you never reach the 4KB limit for the session cookie and you can save more data.

Now, having mcrypt enabled is sufficient, but upgrading may be worth since they now use HMAC instead of md5 in /system/libraries/Session.php, in particular the _set_cookie(), old code:

// if encryption is not used, we provide an md5 hash to prevent userside tampering
$cookie_data = $cookie_data.md5($cookie_data.$this->encryption_key);

new code:

$cookie_data .= hash_hmac('sha1', $cookie_data, $this->encryption_key);

Still confused about that one. Seriously, there should be a massive disclaimer about using CI sessions! It's ridiculous.

I do understand your frustration.

For once more, please allow me to dissect what is really going on inside the CI session class. I also do understand the assumptions and analysis of the programmer assigned to write this class.

First and foremost, how did this important piece of information made it to the user's cookie bin?

The answer is simple. Because of the method called _set_cookie and this is how it was done

setcookie(
        $this->sess_cookie_name,
        $cookie_data,
        $expire,
        $this->cookie_path,
        $this->cookie_domain,
        $this->cookie_secure
    );

We can encrypt all we want in every possible means, the tiempo of the tango will never change. Why? Because, we are only encrypting the name of the session and the cookie name. Encrypting my name, while exposing the most critical information in the open does not and will not equate to security as they call it.

The screenshot below is the product of the method above

43498189d8755b601254f8cb578a486c

and this is the edited version of what the cookie says. I removed 90% of it, because it is getting pretty detail and I don't intend to be teaching or enticing people on cracking on these things that are supposedly "secret".

cd0200f3a6c62cf916c926b7b827d0fa

I purposely did the screenshot, because I don't want people copy and pasting this stuff running around the web to decode it. The point I am trying to make here is that the last_activity isn't really hard to decode. In fact, the las_activity value for the above translate to this

"last_activity";i:1403837480;

or in human terms, the last_activity was sometime today around

Fri, 27 Jun 2014 02:51:20 GMT

Now, we have encrypted the cookie name all we want., we can even encrypt it three folds over. Will it help? I don't think so.. here are my reasons. I don't care about the encrypted cookie name, I only want last_activity and the User_data values as shown on my screenshot above. Pretty much every validations of something will always check for the time , ip, browser types.

What amazes me the most is on how the application validates, revalidates and updates

// Update the session ID and last_activity field in the DB if needed
    if ($this->sess_use_database === TRUE)
    {
        // set cookie explicitly to only have our session data
        $cookie_data = array();
        foreach (array('session_id','ip_address','user_agent','last_activity') as $val)
        {
            $cookie_data[$val] = $this->userdata[$val];
        }

        $this->CI->db->query($this->CI->db->update_string($this->sess_table_name, array('last_activity' => $this->now, 'session_id' => $new_sessid), array('session_id' => $old_sessid)));
    }

    // Write the cookie
    $this->_set_cookie($cookie_data);

looking at the codes above, the session_id has been held at a constant-like value because it was a random md5 which the application cannot be reverse to double check its validity. The function just blur it out from nowhere. What I can make out about this is that, how about if I can change the unix on my last_activity within the limits of the session expiration? Will it work? maybe just maybe.

I could have go on and on for this subject matter, but I want to keep it at this moderate level of exposure and I will not go beyond for what I already shared to this point.

I'm going to attempt to address all of the points discussed thus far.

Firstly, yes, you will want to change the config setting to encrypt the cookie, otherwise the session data will appear as plaintext within the cookie. I don't know why CodeIgniter requires the encryption key to be set if using sessions, even if using unencrypted session cookies.

DaniWeb uses CodeIgniter sessions, and we also rely heavily on AJAX. I've never experienced any issues with the session library logging people out due to AJAX. However, we do have the session security options to match IP and match useragent both disabled.

We have matching IP disabled because some people use ISPs with frequently changing dynamic IP addresses. It's even possible for the IP address to change from one click of a webpage to another. We don't match the useragent because I learned the hard way that certain versions of Firefox (I believe it was FF, anyhow, but regardless it was one of the mainstream desktop browsers) likes to make a slight change to the useragent string upon each click of a webpage (I think it was timestamp related or something.).

It is most likely matching the user agent that complicates AJAX, although it wasn't AJAX specifically that caused us our issues two years ago.

As veedeoo mentions, be stingy with what you put in session data. Even though it's encrypted, it's not a good idea to put any sensitive data in a cookie as a general rule, and also remember there's a 4K maximum size anyhow.

Without giving away too much into our own security (which I'm quite proud of, incidentally), we actually don't use the session database.

The CodeIgniter session cookie is encrypted and stores simply a hashed string which can be used to log the user in server side ... similar to the functionality of the session ID.

On each page load, the login credentials are checked server side, and we calculate all the necessary permissions and other information about the user. None of that is stored client side at all.

Essentially we do it just as CodeIgniter would if we were to use their database functionality.

We have a separate table (outside of CodeIgniter's session library) that keeps track of the applicable data for currently logged in members. We have no reason to store session information serverside for thousands of guests at a time, which is pretty much the only reason I had to roll my own.

Member Avatar for iamthwee

Thanks guys that has answered a few of my questions.

My controllers work as such:

if (login == success)
{
  get user id from database and set their session id
  //for example since I am admin I have userid = 1
  //so I set my session

  $this->session_set('userid', '1');
}

Then I use this persistent session id to check for authentication levels in my web application. Obviously, I was concerned that since the code igniter session is client side the user could edit the cookie session and change it.

The reason why I overlooked needing to set cookie_encrypt to TRUE is because I never realised codeigniter sessions are in fact cookies!

So I guess I am right in assuming I don't need to store sessions in the database. As long as I have cookie_encrypt set to true and I am using mcrypt() server side I should be protected.

The user won't be able to change these values? So I won't need to use any of veedeo's extra authentication protocols?

Right?

___
On a side note why the hell does the vanilla install of codeigniter force you to set an encryption key but doesn't force you to set cookie_encrypt to be TRUE.

Isn't that just plain stupid? Or am I again missing something.

On a side note why the hell does the vanilla install of codeigniter force you to set an encryption key but doesn't force you to set cookie_encrypt to be TRUE.

Isn't that just plain stupid? Or am I again missing something.

I think it was An overlooked negligence in plain sight.

No, you don't have to do any of my protocol examples those are semi paranoid. Just use a pretty good encryption and you should be good to go.

commented: thanks +14

I thank you for the information! I was looking for and could not find. You helped me!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.