正确地重新生成会话 ID

Properly regenerating session ID's

我在看 https://www.php.net/manual/en/function.session-regenerate-id.php 因为我想学习如何正确处理会话,所以我使用了这个例子:

<?php
// NOTE: This code is not fully working code, but an example!
// my_session_start() and my_session_regenerate_id() avoid lost sessions by
// unstable network. In addition, this code may prevent exploiting stolen
// session by attackers.

function my_session_start() {
    session_start();
    if (isset($_SESSION['destroyed'])) {
       if ($_SESSION['destroyed'] < time()-300) {
           // Should not happen usually. This could be attack or due to unstable network.
           // Remove all authentication status of this users session.
           remove_all_authentication_flag_from_active_sessions($_SESSION['userid']);
           throw(new DestroyedSessionAccessException);
       }
       if (isset($_SESSION['new_session_id'])) {
           // Not fully expired yet. Could be lost cookie by unstable network.
           // Try again to set proper session ID cookie.
           // NOTE: Do not try to set session ID again if you would like to remove
           // authentication flag.
           session_commit();
           session_id($_SESSION['new_session_id']);
           // New session ID should exist
           session_start();
           return;
       }
   }
}

function my_session_regenerate_id() {
    // New session ID is required to set proper session ID
    // when session ID is not set due to unstable network.
    $new_session_id = session_create_id();
    $_SESSION['new_session_id'] = $new_session_id;
    
    // Set destroy timestamp
    $_SESSION['destroyed'] = time();
    
    // Write and close current session;
    session_commit();

    // Start session with new session ID
    session_id($new_session_id);
    ini_set('session.use_strict_mode', 0);
    session_start();
    ini_set('session.use_strict_mode', 1);
    
    // New session does not need them
    unset($_SESSION['destroyed']);
    unset($_SESSION['new_session_id']);
}
?>

我试着玩了一下这个例子并添加了它,但我注意到一个问题,每当我登录会话时都会一次又一次地被擦除,为什么?

最终代码如下:

<?php
// NOTE: This code is not fully working code, but an example!
// my_session_start() and my_session_regenerate_id() avoid lost sessions by
// unstable network. In addition, this code may prevent exploiting stolen
// session by attackers.

function my_session_start() {
    session_start();
    if (isset($_SESSION['destroyed'])) {
       if ($_SESSION['destroyed'] < time()-300) {
           // Should not happen usually. This could be attack or due to unstable network.
           // Remove all authentication status of this users session.
           remove_all_authentication_flag_from_active_sessions($_SESSION['ID']);
           throw(new DestroyedSessionAccessException);
       }
       if (isset($_SESSION['new_session_id'])) {
           // Not fully expired yet. Could be lost cookie by unstable network.
           // Try again to set proper session ID cookie.
           // NOTE: Do not try to set session ID again if you would like to remove
           // authentication flag.
           session_commit();
           session_id($_SESSION['new_session_id']);
           // New session ID should exist
           session_start();
           return;
       }
   }
}

function my_session_regenerate_id() {
    // New session ID is required to set proper session ID
    // when session ID is not set due to unstable network.
    $new_session_id = session_create_id();
    $_SESSION['new_session_id'] = $new_session_id;
    
    // Set destroy timestamp
    $_SESSION['destroyed'] = time();
    
    // Write and close current session;
    session_commit();

    // Start session with new session ID
    session_id($new_session_id);
    ini_set('session.use_strict_mode', 0);
    session_start();
    ini_set('session.use_strict_mode', 1);
    
    // New session does not need them
    unset($_SESSION['destroyed']);
    unset($_SESSION['new_session_id']);
}
my_session_start();
my_session_regenerate_id();
if(empty($_SESSION['ID'])) header("Location: /login/");
?>

拜托,任何你需要的细节,问我。

  • 最安全的重新生成会话是session_regenerate_id(true);
  • (true) 将删除现有会话并创建新的
    一个
  • 要保留新会话和旧会话直到到期使用
    session_regenerate_id();

session_regenerate_id() php 手册中示例代码的问题是新的 session_id 尚未在数据存储中,因此当 session_start( ) 被调用,没有要加载的会话变量。

我在下面的代码中通过设置一个变量来解决这个问题

$save_session_vars = $_SESSION;

存储会话变量,然后在启动新会话时 反过来:

$_SESSION = $save_session_vars;

您可以使用 session_encode() 和 session_decode 来做同样的事情。

regenerate_id 示例代码中还值得注意的是

ini_set('session.use_strict_mode', 0);
session_start();
ini_set('session.use_strict_mode', 1);

第二个 ini_set 将失败,因为会话已启动。我认为这甚至没有必要,因为 strict_mode 设置会导致 php 在用户提供的 session_id 用于启动会话时检查会话是否在文件中(正是我们所做的做以及为什么 strict_mode 已被关闭)。会话开始后,关闭 strict_mode 可能没有什么坏处。但是,在下面的函数中,我再次关闭会话,以便我可以将对新会话的初始访问标记为重新生成的会话,因此我继续并在会话关闭时将 strict_mode 重置为 1。

这可能很明显,但我会注意到,如果出于某种原因您的应用程序中有多个 session_start(),您需要将所有内容替换为 regen_session_start( ) 所以销毁的标志被检查。

<?php
function regenerate_session_id_unstable_networks() {

    // get a new session_id, then set this value on
    // current session + timestamp in case it is
    // accessed soon because of unstable network not
    // getting new cookie value from our upcoming change.
    // this value is checked in regen_session_start()
    // --------------------------------------------------
    $new_session_id = _sess_create_sid();
    $_SESSION['new_session_id'] = $new_session_id;
    $_SESSION['destroyed'] = time();
    //
    // Write and close current session;
    // ---------------------------------
    session_write_close();
    // We have to turn off strict mode here
    // because new session_id won't be on file
    // and we'll will get another session_id
    // from session_start()
    // --------------------------------------
    $was_in_strict_mode = false;
    if (1 == ini_get('session.use_strict_mode')) {
        ini_set('session.use_strict_mode', 0);
        $was_in_strict_mode = true;
    }
    // save our current session vars as they are
    // still set even though we closed the session
    // --------------------------------------------
    $save_session_vars = $_SESSION;
    // Start session with new session ID. By setting session_id
    // to our new session id we get a new empty session because
    // it's not stored yet, the same point as with ini setting.
    // --------------------------------------------------------
    $strict_mode = ini_get('session.use_strict_mode');
    session_id($new_session_id);
    regen_session_start();
    $cur_session_id = session_id();
    //
    // Restore our saved session vars
    // ------------------------------
    $_SESSION = $save_session_vars;
    //
    // and then remove the breadcrumbs left on old session from new session
    // --------------------------------------------------------------------
    unset($_SESSION['destroyed']);
    unset($_SESSION['new_session_id']);
    //
    // close session. reset strict mode and start session again
    // Note: must change ini settings while session is closed
    // --------------------------------------------------------
    session_write_close();
    if ($was_in_strict_mode) {
        @ini_set('session.use_strict_mode', 1);
    }
    // mark_new_session_as_regenerated_for_forensics();
    regen_session_start();
}


// -------------------------------
//
// -------------------------------
function regen_session_start() {
    $result = session_start();
    // if started session has been flagged destroyed,
    // either take evasive action or activate the new session
    // ------------------------------------------------------
    if (isset($_SESSION['destroyed'])) {

        // see if this access is after 5 minutes of change to session id
        // if so it's suspect so do something about it.
        // ---------------------------------------------------------------
        if ($_SESSION['destroyed'] + 300 < time()) {
            remove_all_authentication_flag_from_active_sessions($_SESSION['userid']);
            do_some_logging_and_take_some_action();
            throw(new DestroyedSessionAccessException);
        }
        // Hasn't been 5 minutes yet so not fully expired.
        // Could be because lost cookie by unstable network
        // (new session cookie value didn't get set on client).
        // Try again to set proper session ID cookie by closing session,
        // setting new session id, and calling session_start.
        // Sort of defeating purpose of key change so some security
        // risk in doing this for too long or at all, adjust to meet needs.
        // ----------------------------------------------------------------
        if (isset($_SESSION['new_session_id'])) {
            //increment_destroyed_access(session_id());
            session_commit();
            session_id($_SESSION['new_session_id']);
            // New session ID should exist
            session_start();
        }
    }
    return $result;
}