Part of the EllisLab Network
   
1 of 3
1
Solution to session data loss when using AJAX
Posted: 17 December 2009 08:15 AM   [ Ignore ]  
Grad Student
Rank
Total Posts:  47
Joined  11-16-2009

Hi All,
This post is a compilation of the steps taken to fix the issue of session updates (using the in-built CI_Session library) while using AJAX in your pages.
(This post assumes you are using the CI session with a database and not just cookies. This solution may work for just cookies too, I am not sure though.)

The CI Session library automatically updates the session information i.e changes the session_id (by default) every 5 mins
(configurable via config.php -> $config[‘sess_time_to_update’]).
While this is a very useful security feature to thwart session hijacking, it can prove to be a bone when using AJAX.

When static pages are accessed, the CI session update does not interfere with regular operation and the new session id is updated in the cookie set in your system, and all is well. But when an AJAX operation is done (get or post), the session is updated and a new session id is generated (and updated in the database) but the cookie in the browser is not updated, so the next time the browser requests a new page it sends a cookie with the wrong session id and the session becomes invalid resulting in loss of session data (person getting kicked out if logged in).

I do not take credit for any of the solutions below, I’m just putting it in one place so that people do not waste as much time as I did trying to solve this. I have given due credit to the posters of the solution but if I have gotten it wrong or not credited you please let me know and I will update the post.

Step 1:
In constants.php (application/config/) you have to add the following line to define an AJAX request.
Taken from WebLee (http://www.weblee.co.uk)

// Define Ajax Request
define('IS_AJAX', isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest');
// 

Step 2:
Create a new file under (application/libraries) called MY_Session.php
The word MY_ should correspond to whatever you have defined in config.php as your prefix for class extension ($config[‘subclass_prefix’])
Paste the following into that file.
Solution by WanWizard

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

class 
MY_Session extends CI_Session 
{

/**
* Update an existing session
*
* @access    public
* @return    void
*/
    
function sess_update()
    
{
       
// skip the session update if this is an AJAX call!
       
if ( !IS_AJAX )
       
{
           parent
::sess_update();
       
}
    } 

}

/* End of file MY_Session.php */
/* Location: ./application/libraries/MY_Session.php */ 


And that’s it!

What we have done is extended the Session Library overriding the update_session function which is responsible for creating new Session ID’s and telling it to only update the session if the current call is not AJAX.

I have tested this and it works,

If there are any errors or better solutions feel free to post them below, so all can benefit from it.

Merry Christmas and Happy Holidays everybody…  grin


George

 Signature 

George
Intractve Solutions

Profile
 
 
Posted: 17 December 2009 08:48 AM   [ Ignore ]   [ # 1 ]  
Lab Technician
Avatar
RankRankRankRank
Total Posts:  1442
Joined  03-10-2009

Very informative.

Thanks you for the clean solution!

 Signature 

Isset | Isset Public Code Repo | Simple Message Library | Session Profiler for CI2.0 | CI session issues in IE

Profile
 
 
Posted: 17 December 2009 09:12 AM   [ Ignore ]   [ # 2 ]  
Sr. Research Associate
Avatar
RankRankRankRankRank
Total Posts:  2774
Joined  07-27-2006

Good stuff, George. Thanks

 Signature 

Check out the Template Library
Oh yeah, I tweet, too (regarding CodeIgniter on occassion).

Profile
 
 
Posted: 13 March 2010 06:17 PM   [ Ignore ]   [ # 3 ]  
Lab Assistant
Avatar
RankRank
Total Posts:  143
Joined  05-23-2009

It worked like a charm, thanks!

 Signature 

thomashunter.name

Profile
 
 
Posted: 10 May 2010 08:33 AM   [ Ignore ]   [ # 4 ]  
Summer Student
Total Posts:  1
Joined  10-31-2007

Thxs! Exactly what I was looking for!

Profile
 
 
Posted: 27 July 2010 02:49 AM   [ Ignore ]   [ # 5 ]  
Summer Student
Total Posts:  5
Joined  04-08-2009

I wish I had found this thread two days ago!!

Profile
 
 
Posted: 11 October 2010 05:31 PM   [ Ignore ]   [ # 6 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  683
Joined  03-21-2009

Hi guys,

This is a great thread and has helped me identify the bug I’ve been chasing.  I did some testing, however, and confirmed that upon return of an ajax call, the browser DOES INDEED get the returned cookie.

Then it occurred to me that the problem could be caused by echo()‘ing the JSON data directly, since it’s likely that the output is sent before the cookie header… but in my application, I’m using a view to send JSON results.

I’m indeed experiencing the problem, as my application will just randomly invalidate my session and it’s definitely during an AJAX call (my login box comes back as the returned AJAX data).

Can anyone shed more light to the cause of this problem?  I’ve found other threads referencing this as well but none seem to conclude anything definitive.

Thanks!

Profile
 
 
Posted: 11 October 2010 06:49 PM   [ Ignore ]   [ # 7 ]  
Sr. Research Associate
Avatar
RankRankRankRankRank
Total Posts:  4108
Joined  11-04-2008

Check the value of the cookie sent to the server by the browser when this happens. Is it the same as the one originally received by the page, or is it the last one received via the ajax call?

 Signature 

WanWizard.eu | Modular CI, an HMVC solution | DataMapper ORM

Profile
 
 
Posted: 11 October 2010 07:29 PM   [ Ignore ]   [ # 8 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  683
Joined  03-21-2009

I stumbled on this thread after the last instance of the issue, so I haven’t been able to do that yet, but I will.  During my testing, I setup a simple static page with an AJAX call to a server-side script.  In the server-side script I set a cookie.  Upon visiting the static page for the first time, the AJAX call fires and the cookie gets set.

I’m convinced this problem is AJAX related, and it’s sporadic enough that it definitely seems to be also related to the 5 minute session id regeneration.  If AJAX calls do set cookies though, what causes the actual problem?

Profile
 
 
Posted: 11 October 2010 08:47 PM   [ Ignore ]   [ # 9 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  683
Joined  03-21-2009

I think I understand the issue, will anyone confirm?  It’s more of a race condition than anything, but it just so happens that the conditions are more likely repeated for applications that have many concurrent AJAX calls, which mine does.

Anytime two requests are returned in a different order than they are sent, your application has the potential of destroying the session (if the regeneration happens after the first request).  Since AJAX is asynchronous, there are plenty of opportunities for this to happen.  In my application, I’m making two AJAX requests at the same time, one of which usually takes longer since it returns more data.

The proposed solution above will decrease the likelihood of this problem occurring for AJAX heavy applications, but in applications that serve assets this could also creep up.  It seems that CodeIgniter’s Session library is not suitable for applications that require concurrent requests from a single user.  This is unfortunate.

Profile
 
 
Posted: 12 October 2010 02:33 AM   [ Ignore ]   [ # 10 ]  
Sr. Research Associate
Avatar
RankRankRankRankRank
Total Posts:  4108
Joined  11-04-2008

From an application point of view it is impossible to cater for this, since you will never know what the last request is (when it would be safe to rotate the id).

Hence the solution simply not to rotate session id’s on ajax calls, but only on page requests.

 Signature 

WanWizard.eu | Modular CI, an HMVC solution | DataMapper ORM

Profile
 
 
Posted: 12 October 2010 08:29 AM   [ Ignore ]   [ # 11 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  683
Joined  03-21-2009

But the problem is not limited to AJAX calls.  A user opening two different links in new tabs could also face this scenario.

What if a session kept two different session IDs and only regenerated one at a time?  Then, a session could be destroyed only if neither session ID matched.  If one session ID matches, the cookie would be updated to reflect both current session IDs.

This would still require a third party to know one of the session IDs, and it would still allow the user’s session IDs to be regenerated without the ugly side effect of inadvertently destroying the user’s session.  What are your thoughts on this approach?

Thanks

Profile
 
 
Posted: 12 October 2010 09:52 AM   [ Ignore ]   [ # 12 ]  
Sr. Research Associate
Avatar
RankRankRankRankRank
Total Posts:  4108
Joined  11-04-2008

I had that implemented at one point. But reverted everything, since it doesn’t solve anything. You just postpone the problem, as after two ID rotations, the original ID would have disappeared.

How do you know it’s not the ajax call? This issue is that an ajax call causes the ID rotation, without a proper cookie update client side. Every action you do after that, from a page reload to clicking a link, will send the now outdated cookie to the server, causing a ‘loss of session’.

 Signature 

WanWizard.eu | Modular CI, an HMVC solution | DataMapper ORM

Profile
 
 
Posted: 12 October 2010 10:06 AM   [ Ignore ]   [ # 13 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  683
Joined  03-21-2009

I don’t believe that to be the exact case.  An AJAX call DOES update the client’s cookie.  The problem is this:

If your session ID is old enough to need regeneration and you issue 2 requests at the same time, the first request hits the server and causes the session class to regenerate the id.  The problem is that the second request has already left the client with an invalid session ID before the first request is able to bring the new session ID back to the browser.  The second request then becomes the culprit - causing your session to be destroyed.

In essence, it’s a race condition.  You can’t really solve the problem by removing the call to sess_update() on AJAX calls, since the same scenario can happen without AJAX.  For instance, if you protect your assets by serving them through a controller, your pages will be making many concurrent requests to the session class and you’ll likely see this problem often.

Profile
 
 
Posted: 12 October 2010 10:13 AM   [ Ignore ]   [ # 14 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  683
Joined  03-21-2009

Here’s a quick test to see that the browser does indeed receive the cookies sent back from an AJAX request:
http://aaroncicali.com/session.html

Profile
 
 
Posted: 12 October 2010 11:03 AM   [ Ignore ]   [ # 15 ]  
Sr. Research Associate
Avatar
RankRankRankRankRank
Total Posts:  4108
Joined  11-04-2008

Ah, ok, I get it.

Question is, how to fix this. Because if you keep the previous session ID, you increase the chance of collisions, and effectively you only double the ID rotation time. If it it’s set to 300 seconds, you now loose both ID’s after 600 seconds. Is that sufficient?

I can see if I can dig up my modified session library from ‘the repo archive’, because it did solve this issue.

 Signature 

WanWizard.eu | Modular CI, an HMVC solution | DataMapper ORM

Profile
 
 
   
1 of 3
1