0, 'path' => '/', 'secure' => isset($_SERVER['HTTPS']), 'httponly' => true, 'samesite' => 'Lax', ]); session_start(); } } /** Redirige a /login si no hay sesión activa. */ public static function require(): void { self::start(); if (empty($_SESSION['user_id'])) { header('Location: /login'); exit; } } public static function login(array $user): void { self::start(); session_regenerate_id(true); $_SESSION['user_id'] = $user['id']; $_SESSION['user'] = [ 'id' => $user['id'], 'name' => $user['name'], 'email' => $user['email'], ]; } public static function logout(): void { self::start(); $_SESSION = []; if (ini_get('session.use_cookies')) { $p = session_get_cookie_params(); setcookie(session_name(), '', time() - 42000, $p['path'], $p['domain'], $p['secure'], $p['httponly']); } session_destroy(); } public static function user(): array { return $_SESSION['user'] ?? []; } public static function csrfToken(): string { self::start(); if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } return $_SESSION['csrf_token']; } public static function validateCsrf(): void { $token = $_POST['_token'] ?? ''; if (!isset($_SESSION['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $token)) { http_response_code(403); exit('Token CSRF inválido.'); } } }