From 1e8329986ef4d06a9bb7550e24f4dacc7715fb5b Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 4 May 2021 17:32:26 +0200 Subject: [main+Session] Move session from /tmp/ to session table --- inc/session.inc.php | 133 ++++++++++++++++++++++++++++++++-------------------- inc/user.inc.php | 10 ++-- inc/util.inc.php | 1 - 3 files changed, 86 insertions(+), 58 deletions(-) (limited to 'inc') diff --git a/inc/session.inc.php b/inc/session.inc.php index cb52cd38..f06cd580 100644 --- a/inc/session.inc.php +++ b/inc/session.inc.php @@ -2,18 +2,17 @@ require_once('config.php'); -@mkdir(CONFIG_SESSION_DIR, 0700, true); -@chmod(CONFIG_SESSION_DIR, 0700); -if (!is_writable(CONFIG_SESSION_DIR)) die('Config error: Session Path not writable!'); - class Session { private static $sid = false; private static $data = false; + private static $dataChanged = false; + private static $userId = 0; - private static function generateSessionId($salt) + private static function generateSessionId(string $salt) { - if (self::$sid !== false) Util::traceError('Error: Asked to generate session id when already set.'); + if (self::$sid !== false) + Util::traceError('Error: Asked to generate session id when already set.'); self::$sid = sha1($salt . ',' . mt_rand(0, 65535) . $_SERVER['REMOTE_ADDR'] @@ -27,26 +26,40 @@ class Session ); } - public static function create($salt = '') + public static function create(string $salt, int $userId, bool $fixedAddress) { self::generateSessionId($salt); - self::$data = array(); + self::$data = []; + self::$userId = $userId; + Database::exec("INSERT INTO session (sid, userid, dateline, lastip, fixedip, data) + VALUES (:sid, :userid, 0, '', :fixedip, '')", [ + 'sid' => self::$sid, + 'userid' => $userId, + 'fixedip' => $fixedAddress ? 1 : 0, + ]); } - public static function load() + public static function load(): bool { // Try to load session id from cookie - if (!self::loadSessionId()) return false; + if (!self::loadSessionId()) + return false; // Succeeded, now try to load session data. If successful, job is done - if (self::readSessionData()) return true; + if (self::readSessionData()) + return true; // Loading session data failed - self::delete(); return false; } - public static function get($key) + public static function getUserId(): int + { + return self::$userId; + } + + public static function get(string $key) { - if (!isset(self::$data[$key]) || !is_array(self::$data[$key])) return false; + if (!isset(self::$data[$key]) || !is_array(self::$data[$key])) + return false; return self::$data[$key][0]; } @@ -55,30 +68,37 @@ class Session * @param mixed $value data to store for key, false = delete * @param int|false $validMinutes validity in minutes, or false = forever */ - public static function set($key, $value, $validMinutes = false) + public static function set(string $key, $value, $validMinutes = 60) { - if (self::$data === false) Util::traceError('Tried to set session data with no active session'); + if (self::$data === false) + Util::traceError('Tried to set session data with no active session'); if ($value === false) { unset(self::$data[$key]); } else { self::$data[$key] = [$value, $validMinutes === false ? false : time() + $validMinutes * 60]; } + self::$dataChanged = true; } - private static function loadSessionId() + private static function loadSessionId(): bool { - if (self::$sid !== false) die('Error: Asked to load session id when already set.'); - if (empty($_COOKIE['sid'])) return false; + if (self::$sid !== false) + Util::traceError('Error: Asked to load session id when already set.'); + if (empty($_COOKIE['sid'])) + return false; $id = preg_replace('/[^a-zA-Z0-9]/', '', $_COOKIE['sid']); - if (empty($id)) return false; + if (empty($id)) + return false; self::$sid = $id; return true; } public static function delete() { - if (self::$sid === false) return; - @unlink(self::getSessionFile()); + if (self::$sid === false) + return; + Database::exec("DELETE FROM session WHERE sid = :sid", + ['sid' => self::$sid]); self::deleteCookie(); self::$sid = false; self::$data = false; @@ -88,47 +108,58 @@ class Session { Util::clearCookie('sid'); } - - private static function getSessionFile() - { - if (self::$sid === false) Util::traceError('Error: Tried to access session file when no session id was set.'); - return CONFIG_SESSION_DIR . '/' . self::$sid; - } - private static function readSessionData() + private static function readSessionData(): bool { - if (self::$data !== false) Util::traceError('Tried to call read session data twice'); - $sessionfile = self::getSessionFile(); - if (!is_readable($sessionfile) || filemtime($sessionfile) + CONFIG_SESSION_TIMEOUT < time()) { - @unlink($sessionfile); - return false; - } - self::$data = @unserialize(@file_get_contents($sessionfile)); - if (self::$data === false) - return false; + if (self::$data !== false) + Util::traceError('Tried to call read session data twice'); + $row = Database::queryFirst("SELECT userid, dateline, lastip, fixedip, data FROM session WHERE sid = :sid", + ['sid' => self::$sid]); $now = time(); - $save = false; + if ($row === false || $row['dateline'] < $now) { + self::delete(); + return false; + } + if ($row['fixedip'] && $row['lastip'] !== $_SERVER['REMOTE_ADDR']) { + return false; // Ignore but don't invalidate + } + self::$userId = $row['userid']; + self::$data = @json_decode($row['data'], true); + if (!is_array(self::$data)) { + self::$data = []; + } foreach (array_keys(self::$data) as $key) { if (self::$data[$key][1] !== false && self::$data[$key][1] < $now) { unset(self::$data[$key]); - $save = true; + self::$dataChanged = true; } } - if ($save) { - self::save(); - } return true; } - public static function save() + public static function saveInternal() { - if (self::$sid === false || self::$data === false) return; //Util::traceError('Called saveSession with no active session'); - $sessionfile = self::getSessionFile(); - $ret = @file_put_contents($sessionfile, @serialize(self::$data)); - if (!$ret) Util::traceError('Storing session data in ' . $sessionfile . ' failed.'); - Util::clearCookie('sid'); - $ret = setcookie('sid', self::$sid, time() + CONFIG_SESSION_TIMEOUT, null, null, !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off', true); - if (!$ret) Util::traceError('Error: Could not set Cookie for Client (headers already sent)'); + $now = time(); + $args = [ + 'dateline' => $now + CONFIG_SESSION_TIMEOUT, + 'lastip' => $_SERVER['REMOTE_ADDR'], + ]; + if (self::$dataChanged) { + $args['data'] = json_encode(self::$data); + } + $query = "UPDATE session SET " . implode(', ', array_map(function ($key) { + return "$key = :$key"; + }, array_keys($args))) . " WHERE sid = :sid"; + $args['sid'] = self::$sid; + Database::exec($query, $args); + $ret = setcookie('sid', self::$sid, $now + CONFIG_SESSION_TIMEOUT, + null, null, !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off', true); + if (!$ret) + Util::traceError('Error: Could not set Cookie for Client (headers already sent)'); } + } +register_shutdown_function(function () { + Session::saveInternal(); +}); diff --git a/inc/user.inc.php b/inc/user.inc.php index 2ad256af..46cc6012 100644 --- a/inc/user.inc.php +++ b/inc/user.inc.php @@ -113,8 +113,8 @@ class User if (self::isLoggedIn()) return true; if (Session::load()) { - $uid = Session::get('uid'); - if ($uid === false || $uid < 1) + $uid = Session::getUserId(); + if ($uid < 1) self::logout(); self::$user = Database::queryFirst('SELECT * FROM user WHERE userid = :uid LIMIT 1', array(':uid' => $uid)); if (self::$user === false) @@ -149,8 +149,7 @@ class User return false; if (!Crypto::verify($pass, $ret['passwd'])) return false; - Session::create($ret['passwd']); - Session::set('uid', $ret['userid']); + Session::create($ret['passwd'], $ret['userid'], false); Session::set('token', md5($ret['passwd'] . ',' . rand() . ',' . time() . ',' @@ -159,8 +158,7 @@ class User . rand() . ',' . $_SERVER['REMOTE_PORT'] . ',' . rand() . ',' - . $_SERVER['HTTP_USER_AGENT'])); - Session::save(); + . $_SERVER['HTTP_USER_AGENT']), false); return true; } diff --git a/inc/util.inc.php b/inc/util.inc.php index fbc894df..81c7d807 100644 --- a/inc/util.inc.php +++ b/inc/util.inc.php @@ -158,7 +158,6 @@ SADFACE; if ($location === false) { $location = preg_replace('/([&?])message\[\]\=[^&]*/', '\1', $_SERVER['REQUEST_URI']); } - Session::save(); $messages = Message::toRequest(); if ($preferRedirectPost && ($redirect = Request::post('redirect', false, 'string')) !== false -- cgit v1.2.3-55-g7522