From bb6fe3612b6dc136e58d668d1c702308748e6bd6 Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Wed, 10 Sep 2025 21:37:04 -0500 Subject: [PATCH] implement session and CSRF handling --- context.php | 5 ++ session.php | 186 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 session.php diff --git a/context.php b/context.php index 297b01a..bfe31ed 100644 --- a/context.php +++ b/context.php @@ -1,5 +1,7 @@ request = new Request(); $this->response = new Response(); + $this->session = new Session(); + $this->session->start(); } /** diff --git a/session.php b/session.php new file mode 100644 index 0000000..d38a88f --- /dev/null +++ b/session.php @@ -0,0 +1,186 @@ +started || session_status() === PHP_SESSION_ACTIVE) { + $this->started = true; + $this->loadFlashData(); + return; + } + + session_start(); + $this->started = true; + $this->loadFlashData(); + } + + /** + * get retrieves a value from the session + */ + public function get(string $key, mixed $default = null): mixed + { + $this->ensureStarted(); + return $_SESSION[$key] ?? $default; + } + + /** + * set stores a value in the session + */ + public function set(string $key, mixed $value): void + { + $this->ensureStarted(); + $_SESSION[$key] = $value; + } + + /** + * has checks if a key exists in the session + */ + public function has(string $key): bool + { + $this->ensureStarted(); + return isset($_SESSION[$key]); + } + + /** + * remove deletes a value from the session + */ + public function remove(string $key): void + { + $this->ensureStarted(); + unset($_SESSION[$key]); + } + + /** + * flash sets a flash message that will be available only for the next request + */ + public function flash(string $key, mixed $value): void + { + $this->ensureStarted(); + $_SESSION['_flash_new'][$key] = $value; + } + + /** + * getFlash retrieves a flash message (available only for current request) + */ + public function getFlash(string $key, mixed $default = null): mixed + { + return $this->flashData[$key] ?? $default; + } + + /** + * hasFlash checks if a flash message exists + */ + public function hasFlash(string $key): bool + { + return isset($this->flashData[$key]); + } + + /** + * csrfToken generates or retrieves the CSRF token for the session + */ + public function csrfToken(): string + { + $this->ensureStarted(); + + if (!isset($_SESSION['_csrf_token'])) { + $_SESSION['_csrf_token'] = bin2hex(random_bytes(32)); + } + + return $_SESSION['_csrf_token']; + } + + /** + * validateCsrf validates a CSRF token + */ + public function validateCsrf(string $token): bool + { + $this->ensureStarted(); + + if (!isset($_SESSION['_csrf_token'])) { + return false; + } + + return hash_equals($_SESSION['_csrf_token'], $token); + } + + /** + * regenerate regenerates the session ID for security + */ + public function regenerate(bool $deleteOldSession = true): bool + { + $this->ensureStarted(); + return session_regenerate_id($deleteOldSession); + } + + /** + * destroy destroys the session + */ + public function destroy(): void + { + $this->ensureStarted(); + + $_SESSION = []; + + if (ini_get("session.use_cookies")) { + $params = session_get_cookie_params(); + setcookie(session_name(), '', time() - 42000, + $params["path"], $params["domain"], + $params["secure"], $params["httponly"] + ); + } + + session_destroy(); + $this->started = false; + $this->flashData = []; + } + + /** + * clear removes all session data but keeps the session active + */ + public function clear(): void + { + $this->ensureStarted(); + $_SESSION = []; + $this->flashData = []; + } + + /** + * all returns all session data + */ + public function all(): array + { + $this->ensureStarted(); + $data = $_SESSION; + unset($data['_flash_old'], $data['_flash_new'], $data['_csrf_token']); + return $data; + } + + /** + * ensureStarted ensures the session is started + */ + private function ensureStarted(): void + { + if (!$this->started) $this->start(); + } + + /** + * loadFlashData loads flash messages and rotates them + */ + private function loadFlashData(): void + { + $this->flashData = $_SESSION['_flash_old'] ?? []; + + $_SESSION['_flash_old'] = $_SESSION['_flash_new'] ?? []; + unset($_SESSION['_flash_new']); + } +}