getJSON和session_regenerate_id()

Vin*_*ier 8 php session jquery getjson

我正在从受会话保护的页面执行标准的getJSON查询:

$.getJSON('queries.php',{q: 'updateEvent', param1: p1},
    function(data){
        ...
    }
);
Run Code Online (Sandbox Code Playgroud)

在我的会话构造函数中,我设置了以下内容:

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);   
}
Run Code Online (Sandbox Code Playgroud)

如果我设置SESSION_REGENERATE_ID为true,那么我的getJSON发送一个令牌,但收到一个令牌,使请求失败.所以目前我正处理SESSION_REGENERATE_ID设置为假.

有没有办法让getJSON在这种条件下工作?

编辑:所有文件都在同一个域下.

我们有index.php,其中包含js,我们有queries.php这是由ajax请求调用的php文件,我们有s_session.php,其中包含上面编写的构造函数.

文件index.html和queries.php都以这种方式受到保护:

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

PHPSESSID位于set-cookie下的ajax请求的标头中.在答案中返回的PHPSESSID是不同的,正如session_regenerate_id所期望的那样.

如果SESSION_REGENERATE_ID设置为FALSE,请求将顺利通过.如果它设置为TRUE,那么我收到错误消息"登录失败".

这是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;
}
Run Code Online (Sandbox Code Playgroud)

编辑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;
}

}
Run Code Online (Sandbox Code Playgroud)

编辑3:这是请求标头和响应cookie:

在此输入图像描述 在此输入图像描述

我注意到在onload成功期间执行的第一个getJSON .在用户之后完成的所有其他操作都是不成功的

Nar*_*arf 2

这主要是由竞争条件引起的,但也可能是浏览器错误。

排除浏览器错误的情况,但所提供的信息存在冲突,更具体地说,在此评论中:

这是根据用户操作进行的多次调用,而不是同时进行。

如果请求从未同时执行,那么这只可能意味着您的浏览器无法正常运行并且发生以下情况之一:

  • 丢弃Set-Cookie它在响应中收到的标头(如果该逻辑依赖于该HttpOnly标志,这可以解释为什么网络仍然有效:D)
  • onLoad事件实际上是在页面加载期间执行的(我知道这没有意义,但如果它是浏览器错误,一切皆有可能)

当然,这些不太可能发生,所以我倾向于说您实际上一次处理多个 AJAX 请求,在这种情况下,竞争条件是一个合理的场景:

  1. 第一个请求开始(使用您的初始 PHPSESSID)
  2. 第二个请求开始(同样,使用相同的 PHPSESSID)
  3. 第一个请求已处理并收到带有新 PHPSESSID 的响应
  4. 第二个请求到目前为止被阻止(会话处理程序使用锁定来防止多个进程同时修改相同的数据),并且刚刚开始使用初始 PHPSESSID进行处理,此时该请求无效,因此它会触发注销。

我个人会查看该事件触发的内容onLoad- 很容易将所有初始化逻辑放在那里并忘记这可能包括多个异步请求。


不管怎样,你真正的逻辑错误是这段代码:

if ( SESSION_REGENERATE_ID )
    session_regenerate_id(SESSION_REGENERATE_ID);
Run Code Online (Sandbox Code Playgroud)

您对两个不同的条件使用相同的值:

  1. 确定是否重新生成会话 ID
  2. 判断session_regenerate_id()是否应立即销毁与旧会话 ID 关联的数据

立即销毁该数据的选项正是为异步请求的这些竞争条件提供解决方案,因为它们实际上是不可避免的。无论您如何努力避免竞争条件,总会在某个时刻发生 - 即使没有逻辑缺陷,网络滞后(例如)仍然可能会触发它。
保留旧会话的数据(当然是暂时的)可以通过简单地允许“迟到”或“不同步”请求来处理启动时可用的任何数据来解决该问题。

过期的会话稍后将由会话垃圾收集器清理。这可能并不理想,但几乎是需要删除数据的存储的唯一解决方案(与 Redis 等缓存存储不同,Redis 允许您设置 TTL 值而不必手动删除)。

就我个人而言,我更喜欢避免会话 ID 重新生成,尤其是在 AJAX 请求期间……正如您所看到的,这是一个蠕虫病毒。:)

  • “正如之前在评论中所解释的,session_regenerate_id 不会破坏会话数据,只会更改 PHPSESSID” - 问题是......数据随新会话 ID 一起保留,但它是旧数据的“副本”。当您调用“session_regenerate_id(true)”时,旧副本将被销毁,这就是使会话无效的原因。只需将其更改为“false”并让垃圾收集器继续其工作即可。:) (2认同)