Welcome Guest, Not a member yet? Register   Sign In
Solution to session data loss when using AJAX

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.

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?


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'.

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.

Here's a quick test to see that the browser does indeed receive the cookies sent back from an AJAX request:

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.

Collisions are one concern. I haven't dug through an extensive amount of native CI code, but I always just assume it to be of the highest quality. That being said, I'm a little surprised at the code I see in the session class.

while (strlen($new_sessid) < 32)
    $new_sessid .= mt_rand(0, mt_getrandmax());

Maybe I'm being picky here, but why calculate a string's length 32 times? Since it's a fixed length, it would definitely be faster using a for() loop:
for($i = 0; $i < 32; $i++)
    $new_sessid .= mt_rand(0, mt_getrandmax());

'session_id'     => md5(uniqid($sessid, TRUE))
By hashing a uniqid(), isn't it actually INCREASING the likelihood of a collision?

In attachment the session library used in ExiteCMS, which uses a previous_id field to save the session_id when it gets rotated.
It's a drop-in replacement for CI's session library (in application/libraries).

Some remarks:
- it's a CI 2.0 library, I haven't tested in in CI 1.7.2.
- it uses a different sessions table layout, you need to recreate the table
- it has sess_write() disabled by default
- it fixes a bug in the original where a custom flashkey is destroyed when updating flash variables

With regards to sess_write(): You need to call it with a parameter TRUE to write the session. ExiteCMS does this at the end of every page request, and when redirecting, so per page request only one session update is done (the original library updates the record every time you modify a session variable). If you don't want that, remove the check of this parameter in sess_write().

[quote author="slowgary" date="1286917314"]
'session_id'     => md5(uniqid($sessid, TRUE))
By hashing a uniqid(), isn't it actually INCREASING the likelihood of a collision?[/quote]

Uniqueid() basically returns a hex representation based on the current time up to the microsecond. The chances of an MD5 collision on a 184bit value are quite small.

I assume that two unqiueid's generated at the same microsecond would be identical, given the algorithm. Which would also generate the same MD5. So I think the answer should be Yes. Question is, how likely is this, and would the randomizer produce a better result?

Is there anybody who could make session2.0 work on CI 1.7.3.; I couldn't ...

Theme © iAndrew 2016 - Forum software by © MyBB