简体   繁体   English

getJSON和session_regenerate_id()

[英]getJSON and session_regenerate_id()

I am performing a standard getJSON query from a page which is session protected: 我正在从受会话保护的页面执行标准的getJSON查询:

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

On my session constructor I have 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 sends a token, but receives a different one, making the request fail. 如果我将SESSION_REGENERATE_ID设置为true,那么我的getJSON将发送一个令牌,但接收到另一个令牌,从而使请求失败。 So for the moment I'm dealing with SESSION_REGENERATE_ID set to false. 因此,目前我正在将SESSION_REGENERATE_ID设置为false。

Is there a way to make getJSON work in such conditions ? 有没有办法让getJSON在这样的条件下工作?

EDIT : all files are under the same domain. 编辑:所有文件都在同一个域下。

We have index.php where the js is included, we have queries.php which is the php file called by the ajax requests, we have s_session.php which includes the constructor written above. 我们在其中包含js的index.php中,有在ajax请求调用的php文件中的querys.php,在其中包含上述构造函数的s_session.php中。

Files index.html and queries.php are both protected at the begining this way : 首先,文件index.html和query.php都受到保护:

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

The PHPSESSID is in the header of the ajax request under set-cookie. PHPSESSID在set-cookie下的ajax请求的标头中。 The PHPSESSID returned in the answer is different, as expected from session_regenerate_id. 答案中返回的PHPSESSID与session_regenerate_id所期望的不同。

If SESSION_REGENERATE_ID is set to FALSE the requests are going through without problem. 如果将SESSION_REGENERATE_ID设置为FALSE,则请求将顺利进行。 If it is set to TRUE, then I get the error message "Login failed". 如果将其设置为TRUE,则会收到错误消息“登录失败”。

Here is the isLoggedIn() : 这是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 full ASSession code : 编辑2:这是完整的ASSession代码:

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 response cookie : 编辑3:这是请求标头和响应cookie:

在此处输入图片说明 在此处输入图片说明

I noticed that the very first getJSON which is performed during the onload is successfull. 我注意到在onload期间执行的第一个getJSON成功。 All the others done after and triggered by user are unsuccessfull 在用户触发之后执行的所有其他操作均未成功

This is mostly caused by a race condition, but a browser bug is also possible. 这主要是由竞争条件引起的,但浏览器错误也可能会导致。

I would rule out the browser bug scenario, but there's a conflict in the provided info, more specifically in this comment : 排除浏览器错误的情况,但是所提供的信息有冲突,更具体地讲,在此注释中

It is several calls, made one by one on user action, never simultaneously. 它是几次呼叫,是根据用户操作一个接一个地进行的,永远不会同时进行。

If the requests are never executed simultaneously , then that could only mean that your browser is not functioning properly and one of the following happens: 如果 从未同时执行这些请求,则仅意味着您的浏览器无法正常运行,并且发生以下情况之一:

  • Discarding the Set-Cookie header it receives in the response (if that logic depends on the HttpOnly flag, this would explain why the web still works :D) 丢弃它在响应中收到的Set-Cookie标头(如果该逻辑取决于HttpOnly标志,这将说明为什么Web仍然可以工作:D)
  • The onLoad event in fact executed during the page load (I know that doesn't make sense, but everything's possible if it's a browser bug) 实际上, onLoad事件是在页面加载期间执行的(我知道这没有意义,但如果是浏览器错误,一切皆有可能)

Of course, those are highly unlikely to happen, so I'm inclined to say that you are in fact processing multiple AJAX requests at a time, in which case the race condition is a plausible scenario: 当然,这些可能性极不可能发生,因此我倾向于说您实际上一次要处理多个AJAX请求,在这种情况下,竞争条件是一个合理的情况:

  1. First request starts (with your initial PHPSESSID) 第一个请求开始(使用您的初始PHPSESSID)
  2. Second request starts (again, with the same PHPSESSID) 第二个请求开始(再次,使用相同的PHPSESSID)
  3. First request is processed and receives response with a new PHPSESSID 处理第一个请求并接收带有新PHPSESSID的响应
  4. Second request was blocked until now (the session handler uses locking to prevent multiple processes modifying the same data simultaneously) and is just now starting to be processed with the initial PHPSESSID , which is invalid at this point, hence why it triggers a log-out. 到目前为止,第二个请求已被阻止(会话处理程序使用锁定来防止多个进程同时修改同一数据),并且现在才开始使用初始PHPSESSID进行处理,该操作在此时是无效的,因此为什么触发注销。

I would personally look at what gets triggered by that onLoad event - it's easy to just put all initialization logic in there and forget that this may include multiple asynchronous requests. 我个人来看一下该onLoad事件触发了什么-将所有初始化逻辑放在其中很容易,而忘记了其中可能包含多个异步请求。


Either way, the real logical error on your part is this piece of code: 无论哪种方式,您真正的逻辑错误就是这段代码:

if ( SESSION_REGENERATE_ID )
    session_regenerate_id(SESSION_REGENERATE_ID);

You're using the same value for what are two different conditions: 您在两种不同的情况下使用相同的值:

  1. Determining whether to regenerate the session ID at all 确定是否完全重新生成会话ID
  2. Telling session_regenerate_id() if it should immediately destroy data associated with the old session ID 告诉session_regenerate_id()是否应立即销毁与旧会话ID相关联的数据

The option not do destroy that data immediately is there exactly to provide a solution to these race conditions with asynchronous requests, because they are practically unavoidable. 选择立即销毁该数据就可以为异步请求提供这些竞争条件的解决方案,因为实际上这是不可避免的。 A race condition will happen at some point, regardless of how hard you try to avoid it - even if there's no logical flaw, network lags (for example) may still trigger it. 无论您如何努力避免这种情况,竞赛条件都会在某个时刻发生-即使没有逻辑缺陷,网络延迟(例如)仍然可能触发它。
Preserving the old session's data (temporarily of course) works around that problem by simply allowing a "late" or "out of sync" request to work with whatever data was available at the time it was started. 通过简单地允许“延迟”或“不同步”请求处理开始时可用的任何数据,保留旧会话的数据(当然是暂时的)可以解决该问题。

Expired sessions will be cleaned up later by the session garbage collector. 过期的会话将在以后由会话垃圾收集器清除。 That's possibly not ideal, but pretty much the only solution for storages that require you to delete data (as opposed to cache stores like Redis, which allow you to set a TTL value instead of having to manually delete). 这可能并不理想,但是几乎是需要删除数据的存储的唯一解决方案(与Redis这样的缓存存储相对,后者允许您设置TTL值而不必手动删除)。

Personally, I prefer to avoid session ID regeneration specifically during AJAX requests ... as you can see, it's a can of worms. 就个人而言,我更喜欢避免在AJAX请求期间特别是会话ID的再生……正如您所看到的,它是蠕虫病毒。 :) :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM