正确地重新生成会话 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;
}
我在看 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;
}