getJSON和session_regenerate_id()

时间:2016-01-25 12:55:10

标签: php jquery session getjson

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

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

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

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);   
}

如果我将SESSION_REGENERATE_ID设置为true,那么我的getJSON会发送一个令牌,但会收到另一个令牌,导致请求失败。所以目前我正在处理SESSION_REGENERATE_ID设置为false。

有没有办法让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;
}

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

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

}

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

enter image description here enter image description here

我注意到在onload期间执行的第一个getJSON是成功的。所有其他在用户之后完成并由用户触发的都是不成功的

1 个答案:

答案 0 :(得分:2)

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

排除浏览器错误情况,但提供的信息存在冲突,更具体地说是this comment

  

这是几个关于用户操作的调用,从不同时进行。

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

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

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

  1. 首次请求开始(使用您的初始PHPSESSID)
  2. 第二个请求开始(再次使用相同的PHPSESSID)
  3. 处理第一个请求并使用新的PHPSESSID
  4. 接收响应
  5. 第二个请求被阻止直到现在(会话处理程序使用锁定来防止多个进程同时修改相同的数据)并且刚才开始使用初始PHPSESSID进行处理,此时无效,因此它会触发注销。
  6. 我个人会看看onLoad事件触发的内容 - 将所有初始化逻辑放在那里很容易,并且忘记这可能包含多个异步请求。

    无论哪种方式,你真正的逻辑错误都是这段代码:

    if ( SESSION_REGENERATE_ID )
        session_regenerate_id(SESSION_REGENERATE_ID);
    

    您在两种不同条件下使用相同的值:

    1. 确定是否重新生成会话ID
    2. 如果它应立即销毁与旧会话ID相关联的数据,则告知session_regenerate_id()
    3. 选项确实会立即销毁这些数据,以便通过异步请求为这些竞争条件提供解决方案,因为它们实际上是不可避免的。竞争条件 会在某个时刻发生,无论你多么努力地避免它 - 即使没有逻辑缺陷,网络滞后(例如)仍可能触发它。 /> 保留旧会话的数据(暂时当然)通过简单地允许"迟到"来解决该问题。或"不同步"请求处理启动时可用的任何数据。

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

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