Files
人事系统开发 1ba6efd8ed MRBS 1.12.2 等保2.0二级整改完整提交
包含:登录失败锁定、90天密码有效期、30分钟会话超时、
强制改密、登录审计日志、屏幕水印、企业背景图、
备案信息固定底部、favicon、JS空集合保护、
会话过期体验优化(403 JSON)、display_errors 关闭、
固定 key 根治 Integrity check failed 等全部改动

注意:config.inc.php/.htaccess/.user.ini 含敏感信息,
通过 .gitignore 排除,勿推送到公开仓库。
2026-09-09 16:55:02 +08:00

160 lines
5.7 KiB
PHP

<?php
declare(strict_types=1);
namespace MRBS\Session;
use MRBS\SessionHandler\Cookie;
use MRBS\User;
use function MRBS\auth;
use function MRBS\get_form_var;
use function MRBS\is_ajax;
use function MRBS\str_ends_with_array;
use function MRBS\url_base;
/**
* Uses PHP's built-in session handling.
*/
class SessionPhp extends SessionWithLogin
{
public function __construct()
{
global $auth, $server;
parent::__construct();
// Check to see if we've been inactive for longer than allowed and if so log out the user.
// Don't log out the user if we're in kiosk mode because the kiosk will normally be inactive.
// Note that we cannot use is_kiosk_mode() here as that will create an infinite loop calling session().
if (!empty($auth['session_php']['inactivity_expire_time']) && !isset($_SESSION['kiosk_password_hash']))
{
if (isset($_SESSION['LastActivity']) &&
((time() - $_SESSION['LastActivity']) > $auth['session_php']['inactivity_expire_time']))
{
$this->logoffUser();
}
// Ajax requests don't count as activity, unless it's the special Ajax request used
// to record client side activity.
$activity = get_form_var('activity', 'int');
if ($activity || !is_ajax() || !isset($_SESSION['LastActivity'])) {
$_SESSION['LastActivity'] = time();
}
}
// Move the current page to the last page, so it can be used as a referrer, and store the new current page -
// but only if (a) we are at the top level of the MRBS web directory (eg index.php) so as to eliminate all
// the ./js, ./ajax and ./css pages and (b) this is not otherwise an Ajax request, eg one of the prefetch
// calls to index.php.
if (isset($server['SCRIPT_FILENAME']) && (MRBS_ROOT === dirname($server['SCRIPT_FILENAME'])) &&
!(isset($server['HTTP_X_REQUESTED_WITH']) && ($server['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest')))
{
$this->updatePage($server['REQUEST_URI'] ?? $server['PHP_SELF'] ?? null);
}
}
// If the server has a Referrer-Policy of strict-origin then HTTP_REFERER will be unreliable
// and it is better to use the last page that we have stored in the $_SESSION variable.
public function getReferrer(): ?string
{
$result = parent::getReferrer();
// If the last page is known, and it's well-formed (parse_url() doesn't return false), and it's
// not already a fully-qualified URL (ie the scheme is empty), and we've got a url base that
// isn't the empty string, then return everything after the last '/' from the last page, prefixed
// by the url base, in case there's a proxy in use.
if (isset($_SESSION['last_page']))
{
$last_page = $_SESSION['last_page'];
$scheme = parse_url($last_page, PHP_URL_SCHEME);
if (($scheme === null) && ('' !== ($base = url_base())))
{
// Get everything after the last '/'
$array = explode('/', $last_page);
$result = $base . array_pop($array);
}
}
return $result;
}
public function updatePage(?string $url): void
{
// Don't update the page if the URL as the same as the one we've already got
// stored for this page. This will be the case if the user has refreshed the
// browser, and if we update the page then we'll lose the last page, which is
// sometimes needed for MRBS to know where to go back to.
if (isset($_SESSION['this_page']) && ($url === $_SESSION['this_page']))
{
return;
}
$_SESSION['last_page'] = $_SESSION['this_page'] ?? null;
$_SESSION['this_page'] = $url;
}
public function getCurrentUser() : ?User
{
$result = $_SESSION['user'] ?? null;
// For some unknown reason the integer value 0 is sometimes stored in the session
// variable. It's not clear how this can happen.
if (isset($result) && !(is_object($result) && is_a($result, 'MRBS\User')))
{
trigger_error('$_SESSION["user"] is expected to be a User object, not ' . json_encode($result), E_USER_WARNING);
$result = null;
}
return $result ?? parent::getCurrentUser();
}
protected function logonUser(string $username) : void
{
$user = auth()->getUser($username);
// As a defence against session fixation, regenerate
// the session id and delete the old session.
$this->regenerate();
$_SESSION['user'] = $user;
// Problems have been reported on Windows IIS with session data not being
// written out without a call to session_write_close()
session_write_close();
}
public function logoffUser() : void
{
global $cookie_path_override;
if (ini_get("session.use_cookies"))
{
// Delete the session cookie
Cookie::delete(session_name());
// Delete any cookies which may have previously been set, incorrectly, before the fixes to get_cookie_path (see
// https://github.com/meeting-room-booking-system/mrbs-code/commit/90ceeb8a0bc5f4850065695a3e085114c5ecae8e and
// https://github.com/meeting-room-booking-system/mrbs-code/commit/cb74320048149c4199281b88d50fb69988a41312).
// Note that the problem didn't occur if $cookie_path_override was set.
// In time, once all the incorrect cookies have expired naturally, this block can be deleted.
if (!isset($cookie_path_override))
{
$params = session_get_cookie_params();
$suffixes = array('ajax/', 'js/');
// If the path ends with one of the suffixes we'll already have deleted it above
if (!str_ends_with_array($params['path'], $suffixes))
{
foreach ($suffixes as $suffix)
{
setcookie(session_name(), '', time() - 42000, $params['path'] . $suffix, $params['domain'], $params['secure'], isset($params['httponly']));
}
}
}
}
$this->destroy();
}
}