From b3a2795d66d4f704f9f6e77c3dd8eedebd108bdb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Mar 2026 19:27:54 +0000 Subject: [PATCH 1/4] Initial plan From c21dfad437f1b080e9e2cf4b1c6ffaeceeb6de5a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Mar 2026 19:33:13 +0000 Subject: [PATCH 2/4] Add proper session management (CSRF, secure cookies, session tracking) Co-authored-by: naielv <109038805+naielv@users.noreply.github.com> --- public_html/_incl/logout.php | 12 ++++++++++++ public_html/_incl/tools.auth.php | 3 +++ public_html/_incl/tools.session.php | 11 ++++++++--- public_html/_login.php | 14 ++++++++++++++ public_html/account/index.php | 6 ++++++ 5 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 public_html/_incl/logout.php diff --git a/public_html/_incl/logout.php b/public_html/_incl/logout.php new file mode 100644 index 0000000..5c1cc59 --- /dev/null +++ b/public_html/_incl/logout.php @@ -0,0 +1,12 @@ + time() - 3600, "path" => "/", "httponly" => true, "secure" => true, "samesite" => "Lax"]; +setcookie("auth_user", "", $cookie_options_expired); +setcookie("auth_pass_b64", "", $cookie_options_expired); +session_unset(); +session_destroy(); +header("Location: $redir"); +die(); diff --git a/public_html/_incl/tools.auth.php b/public_html/_incl/tools.auth.php index d61fd94..55d9e81 100644 --- a/public_html/_incl/tools.auth.php +++ b/public_html/_incl/tools.auth.php @@ -39,6 +39,9 @@ if (($_SESSION["auth_ok"] ?? false) != true $_SESSION["auth_user"] = $username; $_SESSION["auth_data"] = db_build_auth_data($row); $_SESSION["auth_ok"] = true; + if (empty($_SESSION["session_created"])) { + $_SESSION["session_created"] = time(); + } init_active_org($_SESSION["auth_data"]); } } diff --git a/public_html/_incl/tools.session.php b/public_html/_incl/tools.session.php index cb4928d..a6012ee 100644 --- a/public_html/_incl/tools.session.php +++ b/public_html/_incl/tools.session.php @@ -1,4 +1,9 @@ 604800 ]); -ini_set("session.use_only_cookies", "true"); -ini_set("session.use_trans_sid", "false"); +ini_set("session.use_only_cookies", "1"); +ini_set("session.use_trans_sid", "0"); +session_start([ + 'cookie_lifetime' => 604800, + 'cookie_httponly' => true, + 'cookie_secure' => true, + 'cookie_samesite' => 'Lax', +]); diff --git a/public_html/_login.php b/public_html/_login.php index c4520c1..e4629e1 100644 --- a/public_html/_login.php +++ b/public_html/_login.php @@ -98,6 +98,7 @@ if (($_GET["google_callback"] ?? "") === "1") { $_SESSION['auth_user'] = $username; $_SESSION['auth_data'] = db_build_auth_data($user_row); $_SESSION['auth_ok'] = true; + $_SESSION['session_created'] = time(); init_active_org($_SESSION['auth_data']); $cookie_options = ["expires" => time() + (86400 * 30), "path" => "/", "httponly" => true, "secure" => true, "samesite" => "Lax"]; setcookie("auth_user", $username, $cookie_options); @@ -141,11 +142,13 @@ if (($_GET["logout"] ?? "") === "1") { $cookie_options_expired = ["expires" => time() - 3600, "path" => "/", "httponly" => true, "secure" => true, "samesite" => "Lax"]; setcookie("auth_user", "", $cookie_options_expired); setcookie("auth_pass_b64", "", $cookie_options_expired); + session_unset(); session_destroy(); header("Location: $redir"); die(); } if (($_GET["clear_session"] ?? "") === "1") { + session_unset(); session_destroy(); $redir = safe_redir($_GET["redir"] ?? "/"); header("Location: $redir"); @@ -154,6 +157,11 @@ if (($_GET["clear_session"] ?? "") === "1") { if (isset($_POST["user"])) { $user = trim(strtolower($_POST["user"])); $password = $_POST["password"]; + // Validate CSRF token + $csrf_token = $_POST["_csrf"] ?? ""; + if (!$csrf_token || !isset($_SESSION["login_csrf"]) || !hash_equals($_SESSION["login_csrf"], $csrf_token)) { + $_GET["_result"] = "Token de seguridad inválido. Por favor, recarga la página e inténtalo de nuevo."; + } else { $row = db_get_user($user); if (!$row || !isset($row["password_hash"])) { $_GET["_result"] = "El usuario no existe."; @@ -162,6 +170,7 @@ if (isset($_POST["user"])) { $_SESSION['auth_user'] = $user; $_SESSION['auth_data'] = db_build_auth_data($row); $_SESSION['auth_ok'] = true; + $_SESSION['session_created'] = time(); init_active_org($_SESSION['auth_data']); $cookie_options = ["expires" => time() + (86400 * 30), "path" => "/", "httponly" => true, "secure" => true, "samesite" => "Lax"]; setcookie("auth_user", $user, $cookie_options); @@ -172,15 +181,20 @@ if (isset($_POST["user"])) { } else { $_GET["_result"] = "La contraseña no es correcta."; } + } } if (strval(db_get_config('installed')) !== '1') { header("Location: /_install.php"); die(); } +if (empty($_SESSION["login_csrf"])) { + $_SESSION["login_csrf"] = bin2hex(random_bytes(32)); +} require_once "_incl/pre-body.php"; ?>
"> + ">

Iniciar sesión en Axia4

diff --git a/public_html/account/index.php b/public_html/account/index.php index 726b237..65f324e 100644 --- a/public_html/account/index.php +++ b/public_html/account/index.php @@ -122,6 +122,12 @@ if ($initials === '') {
ID Sesión
Org. activa
Autenticación
+ +
Sesión iniciada
+ + +
Última actividad
+
Cerrar sesión
From 868b8477e0b05d353c23ad0014e4434c6e879b82 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Mar 2026 19:37:23 +0000 Subject: [PATCH 3/4] Add Dispositivos conectados (connected devices) session tracking Co-authored-by: naielv <109038805+naielv@users.noreply.github.com> --- public_html/_incl/db.php | 85 +++++++++++++++++ public_html/_incl/logout.php | 2 + .../_incl/migrations/004_user_sessions.sql | 17 ++++ public_html/_incl/tools.auth.php | 15 +++ public_html/_login.php | 3 + public_html/account/index.php | 93 +++++++++++++++++++ public_html/account/revoke_session.php | 42 +++++++++ 7 files changed, 257 insertions(+) create mode 100644 public_html/_incl/migrations/004_user_sessions.sql create mode 100644 public_html/account/revoke_session.php diff --git a/public_html/_incl/db.php b/public_html/_incl/db.php index 574c8b9..e728caa 100644 --- a/public_html/_incl/db.php +++ b/public_html/_incl/db.php @@ -769,3 +769,88 @@ function init_active_centro(?array $auth_data = null): void { init_active_org($auth_data); } + +// ── User session helpers (Dispositivos conectados) ──────────────────────────── + +/** + * Register or refresh a session record in user_sessions. + * Stores a SHA-256 hash of the PHP session_id so the raw token never reaches the DB. + */ +function db_register_session(string $username): void +{ + $token = hash('sha256', session_id()); + $ip = $_SERVER['HTTP_X_FORWARDED_FOR'] + ?? $_SERVER['REMOTE_ADDR'] + ?? ''; + // Only keep the first IP if X-Forwarded-For contains a list + $ip = trim(explode(',', $ip)[0]); + $ua = substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 512); + + db()->prepare( + "INSERT INTO user_sessions (session_token, username, ip_address, user_agent) + VALUES (?, ?, ?, ?) + ON CONFLICT(session_token) DO UPDATE SET + last_active = datetime('now'), + username = excluded.username" + )->execute([$token, strtolower($username), $ip, $ua]); +} + +/** Update last_active for the current session. */ +function db_touch_session(): void +{ + $token = hash('sha256', session_id()); + db()->prepare( + "UPDATE user_sessions SET last_active = datetime('now') WHERE session_token = ?" + )->execute([$token]); +} + +/** Delete the current session record from the DB. */ +function db_delete_session(): void +{ + $token = hash('sha256', session_id()); + db()->prepare('DELETE FROM user_sessions WHERE session_token = ?')->execute([$token]); +} + +/** + * Delete a specific session by token for a given user. + * Enforces that the session belongs to the requesting user. + */ +function db_revoke_session(string $token, string $username): void +{ + db()->prepare('DELETE FROM user_sessions WHERE session_token = ? AND username = ?') + ->execute([$token, strtolower($username)]); +} + +/** Delete all session records for a user (e.g. on password change or account wipe). */ +function db_delete_user_sessions(string $username): void +{ + db()->prepare('DELETE FROM user_sessions WHERE username = ?') + ->execute([strtolower($username)]); +} + +/** Return all session rows for a user, newest first. */ +function db_get_user_sessions(string $username): array +{ + $stmt = db()->prepare( + 'SELECT session_token, ip_address, user_agent, created_at, last_active + FROM user_sessions + WHERE username = ? + ORDER BY last_active DESC' + ); + $stmt->execute([strtolower($username)]); + return $stmt->fetchAll(); +} + +/** + * Check whether the current PHP session has a valid record in user_sessions. + * Returns false if the session was revoked or was never registered. + */ +function db_session_is_valid(string $username): bool +{ + $token = hash('sha256', session_id()); + $stmt = db()->prepare( + 'SELECT 1 FROM user_sessions WHERE session_token = ? AND username = ? LIMIT 1' + ); + $stmt->execute([$token, strtolower($username)]); + return $stmt->fetchColumn() !== false; +} diff --git a/public_html/_incl/logout.php b/public_html/_incl/logout.php index 5c1cc59..dcf590c 100644 --- a/public_html/_incl/logout.php +++ b/public_html/_incl/logout.php @@ -1,11 +1,13 @@ time() - 3600, "path" => "/", "httponly" => true, "secure" => true, "samesite" => "Lax"]; setcookie("auth_user", "", $cookie_options_expired); setcookie("auth_pass_b64", "", $cookie_options_expired); +db_delete_session(); session_unset(); session_destroy(); header("Location: $redir"); diff --git a/public_html/_incl/migrations/004_user_sessions.sql b/public_html/_incl/migrations/004_user_sessions.sql new file mode 100644 index 0000000..202a280 --- /dev/null +++ b/public_html/_incl/migrations/004_user_sessions.sql @@ -0,0 +1,17 @@ +-- Axia4 Migration 004: User Sessions (Dispositivos conectados) +-- Tracks active authenticated sessions so users can see and revoke connected devices. + +PRAGMA journal_mode = WAL; +PRAGMA foreign_keys = ON; + +CREATE TABLE IF NOT EXISTS user_sessions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + session_token TEXT UNIQUE NOT NULL, -- SHA-256 hash of the PHP session_id() + username TEXT NOT NULL, + ip_address TEXT NOT NULL DEFAULT '', + user_agent TEXT NOT NULL DEFAULT '', + created_at TEXT NOT NULL DEFAULT (datetime('now')), + last_active TEXT NOT NULL DEFAULT (datetime('now')) +); + +CREATE INDEX IF NOT EXISTS idx_user_sessions_username ON user_sessions (username); diff --git a/public_html/_incl/tools.auth.php b/public_html/_incl/tools.auth.php index 55d9e81..eee350d 100644 --- a/public_html/_incl/tools.auth.php +++ b/public_html/_incl/tools.auth.php @@ -43,6 +43,19 @@ if (($_SESSION["auth_ok"] ?? false) != true $_SESSION["session_created"] = time(); } init_active_org($_SESSION["auth_data"]); + db_register_session($username); + } +} + +// ── Validate session is still active in user_sessions (enables revocation) ─── +if (!empty($_SESSION["auth_ok"]) && !empty($_SESSION["auth_user"]) + && empty($_SESSION["auth_external_lock"]) +) { + if (!db_session_is_valid($_SESSION["auth_user"])) { + session_unset(); + session_destroy(); + header("Location: /_login.php?_result=" . urlencode("Tu sesión fue revocada. Inicia sesión de nuevo.")); + die(); } } @@ -56,6 +69,7 @@ if (!empty($_SESSION["auth_ok"]) && !empty($_SESSION["auth_user"])) { init_active_org($_SESSION["auth_data"]); } $_SESSION["last_reload_time"] = time(); + db_touch_session(); } elseif ($load_mode !== "never") { $last = $_SESSION["last_reload_time"] ?? 0; if (time() - $last > 300) { @@ -65,6 +79,7 @@ if (!empty($_SESSION["auth_ok"]) && !empty($_SESSION["auth_user"])) { init_active_org($_SESSION["auth_data"]); } $_SESSION["last_reload_time"] = time(); + db_touch_session(); } if (!isset($_SESSION["last_reload_time"])) { $_SESSION["last_reload_time"] = time(); diff --git a/public_html/_login.php b/public_html/_login.php index e4629e1..a6c2eb4 100644 --- a/public_html/_login.php +++ b/public_html/_login.php @@ -100,6 +100,7 @@ if (($_GET["google_callback"] ?? "") === "1") { $_SESSION['auth_ok'] = true; $_SESSION['session_created'] = time(); init_active_org($_SESSION['auth_data']); + db_register_session($username); $cookie_options = ["expires" => time() + (86400 * 30), "path" => "/", "httponly" => true, "secure" => true, "samesite" => "Lax"]; setcookie("auth_user", $username, $cookie_options); setcookie("auth_pass_b64", base64_encode($password), $cookie_options); @@ -142,6 +143,7 @@ if (($_GET["logout"] ?? "") === "1") { $cookie_options_expired = ["expires" => time() - 3600, "path" => "/", "httponly" => true, "secure" => true, "samesite" => "Lax"]; setcookie("auth_user", "", $cookie_options_expired); setcookie("auth_pass_b64", "", $cookie_options_expired); + db_delete_session(); session_unset(); session_destroy(); header("Location: $redir"); @@ -172,6 +174,7 @@ if (isset($_POST["user"])) { $_SESSION['auth_ok'] = true; $_SESSION['session_created'] = time(); init_active_org($_SESSION['auth_data']); + db_register_session($user); $cookie_options = ["expires" => time() + (86400 * 30), "path" => "/", "httponly" => true, "secure" => true, "samesite" => "Lax"]; setcookie("auth_user", $user, $cookie_options); setcookie("auth_pass_b64", base64_encode($password), $cookie_options); diff --git a/public_html/account/index.php b/public_html/account/index.php index 65f324e..4745728 100644 --- a/public_html/account/index.php +++ b/public_html/account/index.php @@ -23,6 +23,51 @@ $initials = mb_strtoupper(mb_substr($parts[0] ?? '', 0, 1) . mb_substr($parts[1] if ($initials === '') { $initials = '?'; } + +// Connected devices +$connectedSessions = db_get_user_sessions($username); +$currentToken = hash('sha256', session_id()); + +/** + * Parse a raw User-Agent string into a human-readable browser + OS label. + * Returns an array ['browser' => string, 'os' => string, 'icon' => string]. + */ +function parse_ua(string $ua): array +{ + // OS + $os = 'Desconocido'; + if (stripos($ua, 'Android') !== false) $os = 'Android'; + elseif (stripos($ua, 'iPhone') !== false + || stripos($ua, 'iPad') !== false) $os = 'iOS'; + elseif (stripos($ua, 'Windows') !== false) $os = 'Windows'; + elseif (stripos($ua, 'Macintosh') !== false + || stripos($ua, 'Mac OS') !== false) $os = 'macOS'; + elseif (stripos($ua, 'Linux') !== false) $os = 'Linux'; + elseif (stripos($ua, 'CrOS') !== false) $os = 'ChromeOS'; + + // Browser + $browser = 'Desconocido'; + if (str_starts_with($ua, 'Axia4Auth/')) $browser = 'Axia4 App'; + elseif (stripos($ua, 'Edg/') !== false) $browser = 'Edge'; + elseif (stripos($ua, 'OPR/') !== false + || stripos($ua, 'Opera') !== false) $browser = 'Opera'; + elseif (stripos($ua, 'Chrome') !== false) $browser = 'Chrome'; + elseif (stripos($ua, 'Firefox') !== false) $browser = 'Firefox'; + elseif (stripos($ua, 'Safari') !== false) $browser = 'Safari'; + + // Emoji icon for a quick visual cue + $icon = match ($browser) { + 'Chrome' => '🌐', + 'Firefox' => '🦊', + 'Safari' => '🧭', + 'Edge' => '🔷', + 'Opera' => '🔴', + 'Axia4 App' => '📱', + default => '💻', + }; + + return ['browser' => $browser, 'os' => $os, 'icon' => $icon]; +} ?>
+ + + + +
diff --git a/public_html/account/revoke_session.php b/public_html/account/revoke_session.php new file mode 100644 index 0000000..ae61f9f --- /dev/null +++ b/public_html/account/revoke_session.php @@ -0,0 +1,42 @@ + Date: Sat, 7 Mar 2026 19:40:33 +0000 Subject: [PATCH 4/4] Replace auth_user+auth_pass_b64 cookies with secure opaque remember token Co-authored-by: naielv <109038805+naielv@users.noreply.github.com> --- public_html/_incl/db.php | 43 +++++++++++++++---- public_html/_incl/logout.php | 3 +- .../_incl/migrations/005_remember_token.sql | 12 ++++++ public_html/_incl/tools.auth.php | 42 ++++++++++-------- public_html/_login.php | 17 ++++---- 5 files changed, 82 insertions(+), 35 deletions(-) create mode 100644 public_html/_incl/migrations/005_remember_token.sql diff --git a/public_html/_incl/db.php b/public_html/_incl/db.php index e728caa..7968cfd 100644 --- a/public_html/_incl/db.php +++ b/public_html/_incl/db.php @@ -773,26 +773,53 @@ function init_active_centro(?array $auth_data = null): void // ── User session helpers (Dispositivos conectados) ──────────────────────────── /** - * Register or refresh a session record in user_sessions. + * Register a new session record in user_sessions. + * $remember_token_hash is the SHA-256 hash of the raw remember-me cookie value. * Stores a SHA-256 hash of the PHP session_id so the raw token never reaches the DB. */ -function db_register_session(string $username): void +function db_register_session(string $username, string $remember_token_hash = ''): void { $token = hash('sha256', session_id()); $ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? ''; - // Only keep the first IP if X-Forwarded-For contains a list $ip = trim(explode(',', $ip)[0]); $ua = substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 512); db()->prepare( - "INSERT INTO user_sessions (session_token, username, ip_address, user_agent) - VALUES (?, ?, ?, ?) + "INSERT INTO user_sessions (session_token, username, ip_address, user_agent, remember_token_hash) + VALUES (?, ?, ?, ?, ?) ON CONFLICT(session_token) DO UPDATE SET - last_active = datetime('now'), - username = excluded.username" - )->execute([$token, strtolower($username), $ip, $ua]); + last_active = datetime('now'), + remember_token_hash = COALESCE(excluded.remember_token_hash, remember_token_hash)" + )->execute([$token, strtolower($username), $ip, $ua, $remember_token_hash ?: null]); +} + +/** + * Restore a session from a remember-me token hash. + * Updates the session_token to the current PHP session_id so revocation + * continues to work after the PHP session was re-created. + * Returns the session row (including username) on success, or null if not found. + */ +function db_restore_session_by_remember_token(string $token_hash): ?array +{ + $pdo = db(); + $stmt = $pdo->prepare( + 'SELECT * FROM user_sessions WHERE remember_token_hash = ? LIMIT 1' + ); + $stmt->execute([$token_hash]); + $row = $stmt->fetch(); + if (!$row) { + return null; + } + // Migrate the row to the new PHP session_id + $new_session_token = hash('sha256', session_id()); + $pdo->prepare( + "UPDATE user_sessions + SET session_token = ?, last_active = datetime('now') + WHERE remember_token_hash = ?" + )->execute([$new_session_token, $token_hash]); + return $row; } /** Update last_active for the current session. */ diff --git a/public_html/_incl/logout.php b/public_html/_incl/logout.php index dcf590c..7b1b2a2 100644 --- a/public_html/_incl/logout.php +++ b/public_html/_incl/logout.php @@ -5,8 +5,7 @@ require_once "db.php"; $redir = safe_redir($_GET["redir"] ?? "/"); $cookie_options_expired = ["expires" => time() - 3600, "path" => "/", "httponly" => true, "secure" => true, "samesite" => "Lax"]; -setcookie("auth_user", "", $cookie_options_expired); -setcookie("auth_pass_b64", "", $cookie_options_expired); +setcookie("auth_token", "", $cookie_options_expired); db_delete_session(); session_unset(); session_destroy(); diff --git a/public_html/_incl/migrations/005_remember_token.sql b/public_html/_incl/migrations/005_remember_token.sql new file mode 100644 index 0000000..4502e24 --- /dev/null +++ b/public_html/_incl/migrations/005_remember_token.sql @@ -0,0 +1,12 @@ +-- Axia4 Migration 005: Add remember_token_hash to user_sessions +-- Replaces the auth_user + auth_pass_b64 cookie pair with a secure opaque token. +-- The raw token lives only in the browser cookie; only its SHA-256 hash is stored. + +PRAGMA journal_mode = WAL; +PRAGMA foreign_keys = ON; + +ALTER TABLE user_sessions ADD COLUMN remember_token_hash TEXT DEFAULT NULL; + +CREATE UNIQUE INDEX IF NOT EXISTS idx_user_sessions_remember + ON user_sessions (remember_token_hash) + WHERE remember_token_hash IS NOT NULL; diff --git a/public_html/_incl/tools.auth.php b/public_html/_incl/tools.auth.php index eee350d..04a84fc 100644 --- a/public_html/_incl/tools.auth.php +++ b/public_html/_incl/tools.auth.php @@ -22,28 +22,36 @@ if (str_starts_with($ua, "Axia4Auth/")) { $_SESSION["auth_user"] = $username; $_SESSION["auth_data"] = db_build_auth_data($row); $_SESSION["auth_ok"] = true; - $_COOKIE["auth_user"] = $username; - $_COOKIE["auth_pass_b64"] = base64_encode($userpass); $_SESSION["auth_external_lock"] = "header"; init_active_org($_SESSION["auth_data"]); } -// ── Cookie-based auto-login ─────────────────────────────────────────────────── -if (($_SESSION["auth_ok"] ?? false) != true - && isset($_COOKIE["auth_user"], $_COOKIE["auth_pass_b64"]) -) { - $username = $_COOKIE["auth_user"]; - $userpass = base64_decode($_COOKIE["auth_pass_b64"]); - $row = db_get_user($username); - if ($row && password_verify($userpass, $row['password_hash'])) { - $_SESSION["auth_user"] = $username; - $_SESSION["auth_data"] = db_build_auth_data($row); - $_SESSION["auth_ok"] = true; - if (empty($_SESSION["session_created"])) { - $_SESSION["session_created"] = time(); +// ── Remember-token auto-login ───────────────────────────────────────────────── +// Restores the session from the opaque auth_token cookie (no password stored). +if (($_SESSION["auth_ok"] ?? false) != true && isset($_COOKIE["auth_token"])) { + $expired = ["expires" => time() - 3600, "path" => "/", "httponly" => true, + "secure" => true, "samesite" => "Lax"]; + $raw_token = $_COOKIE["auth_token"]; + $token_hash = hash('sha256', $raw_token); + $sess_row = db_restore_session_by_remember_token($token_hash); + if ($sess_row) { + $username = $sess_row['username']; + $row = db_get_user($username); + if ($row) { + $_SESSION["auth_user"] = $username; + $_SESSION["auth_data"] = db_build_auth_data($row); + $_SESSION["auth_ok"] = true; + if (empty($_SESSION["session_created"])) { + $_SESSION["session_created"] = time(); + } + init_active_org($_SESSION["auth_data"]); + } else { + // User no longer exists — clear the stale cookie + setcookie("auth_token", "", $expired); } - init_active_org($_SESSION["auth_data"]); - db_register_session($username); + } else { + // Token not found (revoked or expired) — clear the stale cookie + setcookie("auth_token", "", $expired); } } diff --git a/public_html/_login.php b/public_html/_login.php index a6c2eb4..21d6cad 100644 --- a/public_html/_login.php +++ b/public_html/_login.php @@ -100,10 +100,11 @@ if (($_GET["google_callback"] ?? "") === "1") { $_SESSION['auth_ok'] = true; $_SESSION['session_created'] = time(); init_active_org($_SESSION['auth_data']); - db_register_session($username); + $remember_token = bin2hex(random_bytes(32)); + $remember_token_hash = hash('sha256', $remember_token); + db_register_session($username, $remember_token_hash); $cookie_options = ["expires" => time() + (86400 * 30), "path" => "/", "httponly" => true, "secure" => true, "samesite" => "Lax"]; - setcookie("auth_user", $username, $cookie_options); - setcookie("auth_pass_b64", base64_encode($password), $cookie_options); + setcookie("auth_token", $remember_token, $cookie_options); $redir = safe_redir($state["redir"] ?? "/"); @@ -141,8 +142,7 @@ if (($_GET["google"] ?? "") === "1") { if (($_GET["logout"] ?? "") === "1") { $redir = safe_redir($_GET["redir"] ?? "/"); $cookie_options_expired = ["expires" => time() - 3600, "path" => "/", "httponly" => true, "secure" => true, "samesite" => "Lax"]; - setcookie("auth_user", "", $cookie_options_expired); - setcookie("auth_pass_b64", "", $cookie_options_expired); + setcookie("auth_token", "", $cookie_options_expired); db_delete_session(); session_unset(); session_destroy(); @@ -168,16 +168,17 @@ if (isset($_POST["user"])) { if (!$row || !isset($row["password_hash"])) { $_GET["_result"] = "El usuario no existe."; } elseif (password_verify($password, $row["password_hash"])) { + $remember_token = bin2hex(random_bytes(32)); + $remember_token_hash = hash('sha256', $remember_token); session_regenerate_id(true); $_SESSION['auth_user'] = $user; $_SESSION['auth_data'] = db_build_auth_data($row); $_SESSION['auth_ok'] = true; $_SESSION['session_created'] = time(); init_active_org($_SESSION['auth_data']); - db_register_session($user); + db_register_session($user, $remember_token_hash); $cookie_options = ["expires" => time() + (86400 * 30), "path" => "/", "httponly" => true, "secure" => true, "samesite" => "Lax"]; - setcookie("auth_user", $user, $cookie_options); - setcookie("auth_pass_b64", base64_encode($password), $cookie_options); + setcookie("auth_token", $remember_token, $cookie_options); $redir = safe_redir($_GET["redir"] ?? "/"); header("Location: $redir"); die();