GetJSON and session_regenerate_id ()

I execute a standard getJSON request from a session protected page:

$.getJSON('queries.php',{q: 'updateEvent', param1: p1}, function(data){ ... } ); 

In my session designer, I set the following:

 function startSession() { ini_set('session.use_only_cookies', SESSION_USE_ONLY_COOKIES); $cookieParams = session_get_cookie_params(); session_set_cookie_params( $cookieParams["lifetime"], $cookieParams["path"], $cookieParams["domain"], SESSION_SECURE, SESSION_HTTP_ONLY ); session_start(); if ( SESSION_REGENERATE_ID ) session_regenerate_id(SESSION_REGENERATE_ID); } 

If I set SESSION_REGENERATE_ID to true, then my getJSON will send a token but get another, which will lead to a request failure. So, at the moment I am dealing with SESSION_REGENERATE_ID set to false.

Is there any way to get getJSON to work in such conditions?

EDIT: all files are in the same domain.

We have index.php where js is included, we have queries.php, which is a php file called by ajax requests, we have s_session.php, which includes the constructor written above.

The index.html and queries.php files are protected at the beginning like this:

 include "s_session.php"; if(!$login->isLoggedIn()) { header('Content-Type: application/json'); echo json_encode(array('content' => 'Login failed')); exit; } 

PHPSESSID is in the ajax request header in the set cookie. The PHPSESSID returned in the response is different, as expected from session_regenerate_id.

If SESSION_REGENERATE_ID is set to FALSE, the queries pass without problems. If set to TRUE, an "Login Error" error message appears.

Here is isLoggedIn ():

 public function isLoggedIn() { //if $_SESSION['user_id'] is not set return false if(ASSession::get("user_id") == null) return false; //if enabled, check fingerprint if(LOGIN_FINGERPRINT == true) { $loginString = $this->_generateLoginString(); $currentString = ASSession::get("login_fingerprint"); if($currentString != null && $currentString == $loginString) return true; else { //destroy session, it is probably stolen by someone $this->logout(); return false; } } $user = new ASUser(ASSession::get("user_id")); return $user->getInfo() !== null; } 

EDIT 2: Here is the complete code:

 class ASSession { /** * Start session. */ public static function startSession() { ini_set('session.use_only_cookies', SESSION_USE_ONLY_COOKIES); session_start(); $s = $_SESSION; $cookieParams = session_get_cookie_params(); session_set_cookie_params( $cookieParams["lifetime"], $cookieParams["path"], $cookieParams["domain"], SESSION_SECURE, SESSION_HTTP_ONLY ); if ( SESSION_REGENERATE_ID ) session_regenerate_id(SESSION_REGENERATE_ID); //$_SESSION = $s; } /** * Destroy session. */ public static function destroySession() { $_SESSION = array(); $params = session_get_cookie_params(); setcookie( session_name(), '', time() - 42000, $params["path"], $params["domain"], $params["secure"], $params["httponly"] ); session_destroy(); } /** * Set session data. * @param mixed $key Key that will be used to store value. * @param mixed $value Value that will be stored. */ public static function set($key, $value) { $_SESSION[$key] = $value; } /** * Unset session data with provided key. * @param $key */ public static function destroy($key) { if ( isset($_SESSION[$key]) ) unset($_SESSION[$key]); } /** * Get data from $_SESSION variable. * @param mixed $key Key used to get data from session. * @param mixed $default This will be returned if there is no record inside * session for given key. * @return mixed Session value for given key. */ public static function get($key, $default = null) { if(isset($_SESSION[$key])) return $_SESSION[$key]; else return $default; } } 

EDIT 3: here are the request headers and the response cookie:

enter image description here enter image description here

I noticed that the very first getJSON that runs during onload is successful. All others made after and called by the user were unsuccessful

+8
jquery php session getjson
source share
1 answer

This is mainly due to the state of the race, but a browser error is also possible.

I would exclude the browser error scenario, but there was a conflict in the information provided, more specifically in this comment :

These are several calls made one after the other by user action, never at the same time.

If the requests are never executed at the same time, this may mean that your browser is not working properly and one of the following events occurs:

  • Dropping the Set-Cookie header that it receives in the response (if this logic depends on the HttpOnly flag, this explains why the web is still working: D)
  • The onLoad event actually fires when the page loads (I know this doesn't make sense, but anything is possible if it's a browser error)

Of course, this is unlikely to happen, so I am inclined to say that you are actually processing several AJAX requests at a time, in which case the race condition is a plausible scenario:

  • The request starts first (with your initial PHPSESSID)
  • The second request is launched (again, with the same PHPSESSID)
  • The first request is processed and receives a response with a new PHPSESSID
  • The second request has been blocked so far (the session handler uses a lock to prevent multiple processes changing the same data from changing at the same time) and is only now starting to process with the original PHPSESSID, which is currently invalid, so it causes a log out.

I would personally see what is caused by this onLoad event - it's easy to just put all the initialization logic in and forget that this can include several asynchronous requests.


In any case, the real logical mistake on your part is part of the code:

 if ( SESSION_REGENERATE_ID ) session_regenerate_id(SESSION_REGENERATE_ID); 

You use the same value for two different conditions:

  • Determining if session id regeneration is needed
  • Report session_regenerate_id() if it should immediately destroy data associated with the old session identifier

The option does not destroy this data, so they should accurately provide a solution to these race conditions with asynchronous requests, since they are almost inevitable. A race condition will occur at some point, no matter how difficult you avoid it - even if there is no logical flaw, network delays (for example) can still cause it.
Saving old session data (temporarily, of course) works around this problem, simply allowing the “late” or “unsynchronized” request to work with any data available at the time of its launch.

Expired sessions will be cleared later by the session garbage collector. This may not be the ideal solution, but pretty much the only storage solution that requires data deletion (unlike cache stores like Redis, which allow you to set the TTL value instead of having to manually delete it).

Personally, I prefer to avoid regenerating the session identifier specifically during AJAX requests ... as you can see, this could be a worm. :)

+2
source share

All Articles