From 35fa27b199ca9d68beae2fc0f76d8f8e6f6440a9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Feb 2026 18:40:16 +0000 Subject: [PATCH 1/3] Initial plan From ffb6b6ce4513a4ca583f0bc602ea1624fb581787 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Feb 2026 18:55:06 +0000 Subject: [PATCH 2/3] Security: fix auth bypass, open redirects, cookie security, OAuth CSRF, and Sf() misuse - Fix critical inverted authentication logic in tools.auth.php (password_verify was inverted) - Fix broken Sf() misuse for username lookups (was always returning empty string) - Add safe_username_to_filename() to tools.security.php for proper username handling - Fix open redirect vulnerability in _login.php for all redirect targets - Add HttpOnly, Secure, SameSite cookie flags to all setcookie() calls - Add CSRF nonce to OAuth state parameter and verify it on callback - Add session_regenerate_id(true) after successful login - Remove redundant session_regenerate_id() from tools.session.php (was called on every request) - Add authentication check to entreaulas/_filefetch.php - Fix broken Sf() usage in entreaulas pages (aulario.php, comedor.php, diario.php, paneldiario.php, proyectos.php, api/comedor.php) - Fix broken Sf() usage in sysadmin/users.php and sysadmin/reset_password.php Co-authored-by: naielv <109038805+naielv@users.noreply.github.com> --- public_html/_incl/tools.auth.php | 36 +++++++---- public_html/_incl/tools.security.php | 26 ++++++++ public_html/_incl/tools.session.php | 1 - public_html/_login.php | 79 +++++++++++++++++------ public_html/entreaulas/_filefetch.php | 5 +- public_html/entreaulas/api/comedor.php | 2 +- public_html/entreaulas/aulario.php | 2 +- public_html/entreaulas/comedor.php | 2 +- public_html/entreaulas/diario.php | 11 ++-- public_html/entreaulas/paneldiario.php | 4 +- public_html/entreaulas/proyectos.php | 86 ++++++++++++------------- public_html/sysadmin/reset_password.php | 2 +- public_html/sysadmin/users.php | 4 +- 13 files changed, 171 insertions(+), 89 deletions(-) diff --git a/public_html/_incl/tools.auth.php b/public_html/_incl/tools.auth.php index 58977ec..e69d494 100644 --- a/public_html/_incl/tools.auth.php +++ b/public_html/_incl/tools.auth.php @@ -8,12 +8,17 @@ $ua = $_SERVER['HTTP_USER_AGENT']; if (str_starts_with($ua, "Axia4Auth/")) { $username = explode("/", $ua)[1]; $userpass = explode("/", $ua)[2]; - $userdata = json_decode(file_get_contents("/DATA/Usuarios/" . Sf($username) . ".json"), true); + $user_filename = safe_username_to_filename($username); + if ($user_filename === "") { + header("HTTP/1.1 403 Forbidden"); + die(); + } + $userdata = json_decode(file_get_contents("/DATA/Usuarios/" . $user_filename . ".json"), true); if (!$userdata) { header("HTTP/1.1 403 Forbidden"); die(); } - if (password_verify($userpass, $userdata["password"])) { + if (!password_verify($userpass, $userdata["password_hash"])) { header("HTTP/1.1 403 Forbidden"); die(); } @@ -30,11 +35,14 @@ if ($_SESSION["auth_ok"] != true && isset($_COOKIE["auth_user"]) && isset($_COOK $username = $_COOKIE["auth_user"]; $userpass_b64 = $_COOKIE["auth_pass_b64"]; $userpass = base64_decode($userpass_b64); - $userdata = json_decode(file_get_contents("/DATA/Usuarios/" . Sf($username) . ".json"), true); - if ($userdata && password_verify($userpass, $userdata["password_hash"])) { - $_SESSION["auth_user"] = $username; - $_SESSION["auth_data"] = $userdata; - $_SESSION["auth_ok"] = true; + $user_filename = safe_username_to_filename($username); + if ($user_filename !== "") { + $userdata = json_decode(file_get_contents("/DATA/Usuarios/" . $user_filename . ".json"), true); + if ($userdata && password_verify($userpass, $userdata["password_hash"])) { + $_SESSION["auth_user"] = $username; + $_SESSION["auth_data"] = $userdata; + $_SESSION["auth_ok"] = true; + } } } @@ -42,8 +50,11 @@ if ($_SESSION["auth_ok"] != true && isset($_COOKIE["auth_user"]) && isset($_COOK if (isset($_SESSION["auth_ok"]) && $_SESSION["auth_ok"] && isset($_SESSION["auth_user"])) { if (isset($AuthConfig["session_load_mode"]) && $AuthConfig["session_load_mode"] === "force") { $username = $_SESSION["auth_user"]; - $userdata = json_decode(file_get_contents("/DATA/Usuarios/" . Sf($username) . ".json"), true); - $_SESSION["auth_data"] = $userdata; + $user_filename = safe_username_to_filename($username); + if ($user_filename !== "") { + $userdata = json_decode(file_get_contents("/DATA/Usuarios/" . $user_filename . ".json"), true); + $_SESSION["auth_data"] = $userdata; + } $_SESSION["last_reload_time"] = time(); } elseif (isset($AuthConfig["session_load_mode"]) && $AuthConfig["session_load_mode"] === "never") { // Do nothing, never reload session data @@ -52,8 +63,11 @@ if (isset($_SESSION["auth_ok"]) && $_SESSION["auth_ok"] && isset($_SESSION["auth $last_reload = $_SESSION["last_reload_time"]; if (time() - $last_reload > 300) { $username = $_SESSION["auth_user"]; - $userdata = json_decode(file_get_contents("/DATA/Usuarios/" . Sf($username) . ".json"), true); - $_SESSION["auth_data"] = $userdata; + $user_filename = safe_username_to_filename($username); + if ($user_filename !== "") { + $userdata = json_decode(file_get_contents("/DATA/Usuarios/" . $user_filename . ".json"), true); + $_SESSION["auth_data"] = $userdata; + } $_SESSION["last_reload_time"] = time(); } } else { diff --git a/public_html/_incl/tools.security.php b/public_html/_incl/tools.security.php index 3b0a3c5..d25061d 100644 --- a/public_html/_incl/tools.security.php +++ b/public_html/_incl/tools.security.php @@ -58,6 +58,32 @@ function Si($input) { return $input; } +function safe_username_to_filename($username) { + /** + * Convert a username (plain username or email) to a safe filename for use in file operations. + * + * Email addresses have @ replaced with __ to match how Google OAuth users are stored. + * The result contains only alphanumeric characters, dots, underscores, and hyphens. + * + * @param string $username The username or email to convert. + * @return string The safe filename (without path or extension), or "" if invalid. + */ + $filename = strtolower((string)$username); + // Remove null bytes + $filename = str_replace("\0", "", $filename); + // Replace @ with __ (to match Google OAuth file naming) + $filename = str_replace("@", "__", $filename); + // Remove any path components to prevent directory traversal + $filename = basename($filename); + // Remove .. sequences + $filename = str_replace("..", "", $filename); + // Keep only alphanumeric, dot, underscore, hyphen + $filename = preg_replace("/[^a-zA-Z0-9._-]/", "_", $filename); + // Trim dots and underscores from ends + $filename = trim($filename, "._"); + return $filename; +} + function Sb($input) { /** * Sanitize a boolean input by converting it to a boolean value. diff --git a/public_html/_incl/tools.session.php b/public_html/_incl/tools.session.php index 62c8a32..cb4928d 100644 --- a/public_html/_incl/tools.session.php +++ b/public_html/_incl/tools.session.php @@ -1,5 +1,4 @@ 604800 ]); -session_regenerate_id(); ini_set("session.use_only_cookies", "true"); ini_set("session.use_trans_sid", "false"); diff --git a/public_html/_login.php b/public_html/_login.php index ca6b678..c459471 100755 --- a/public_html/_login.php +++ b/public_html/_login.php @@ -5,11 +5,30 @@ if (!isset($AuthConfig)) { $AuthConfig = json_decode(file_get_contents("/DATA/AuthConfig.json"), true); } $DOMAIN = $_SERVER["HTTP_X_FORWARDED_HOST"] ?? $_SERVER["HTTP_HOST"]; + +/** + * Return a safe redirect URL: only allow relative paths starting with a single slash. + * Falls back to "/" for any external, protocol-relative, or otherwise unsafe URLs. + */ +function safe_redir($url) { + $url = (string)$url; + // Must start with a single "/" but not "//" (protocol-relative) + if (preg_match('#^/[^/]#', $url) || $url === '/') { + // Strip newlines to prevent header injection + return preg_replace('/[\r\n]/', '', $url); + } + return '/'; +} + if ($_GET["reload_user"] == "1") { - $user = str_replace("@", "__", $_SESSION["auth_user"]); - $userdata = json_decode(file_get_contents("/DATA/Usuarios/" . Sf($user) . ".json"), true); + $user_filename = safe_username_to_filename($_SESSION["auth_user"] ?? ""); + if ($user_filename === "") { + header("Location: /"); + die(); + } + $userdata = json_decode(file_get_contents("/DATA/Usuarios/" . $user_filename . ".json"), true); $_SESSION['auth_data'] = $userdata; - $redir = $_GET["redir"] ?? "/"; + $redir = safe_redir($_GET["redir"] ?? "/"); header("Location: $redir"); die(); } @@ -20,6 +39,15 @@ if ($_GET["google_callback"] == "1") { if (!isset($_GET["code"])) { die("Error: No se recibió el código de autorización de Google."); } + + // Validate CSRF nonce from state parameter + $state_raw = $_GET["state"] ?? ""; + $state = json_decode(base64_decode($state_raw), true); + $state_nonce = $state["nonce"] ?? ""; + if (!$state_nonce || !isset($_SESSION["oauth_nonce"]) || !hash_equals($_SESSION["oauth_nonce"], $state_nonce)) { + die("Error: Estado OAuth inválido. Por favor, inténtalo de nuevo."); + } + unset($_SESSION["oauth_nonce"]); $code = $_GET["code"]; @@ -56,7 +84,11 @@ if ($_GET["google_callback"] == "1") { $email = $user_info["email"]; $name = $user_info["name"] ?? explode("@", $email)[0]; - $userfile = "/DATA/Usuarios/" . Sf(strtolower(str_replace("@", "__", $email))) . ".json"; + $user_filename = safe_username_to_filename($email); + if ($user_filename === "") { + die("Error: Dirección de correo inválida."); + } + $userfile = "/DATA/Usuarios/" . $user_filename . ".json"; $password = bin2hex(random_bytes(16)); // Generar una contraseña aleatoria para el usuario, aunque no se usará para iniciar sesión if (file_exists($userfile)) { $userdata = json_decode(file_get_contents($userfile), true); @@ -72,13 +104,15 @@ if ($_GET["google_callback"] == "1") { file_put_contents($userfile, json_encode($userdata)); } + session_regenerate_id(true); $_SESSION['auth_user'] = $email; $_SESSION['auth_data'] = $userdata; $_SESSION['auth_ok'] = true; - setcookie("auth_user", $email, time() + (86400 * 30), "/"); - setcookie("auth_pass_b64", base64_encode($password), time() + (86400 * 30), "/"); + $cookie_options = ["expires" => time() + (86400 * 30), "path" => "/", "httponly" => true, "secure" => true, "samesite" => "Lax"]; + setcookie("auth_user", $email, $cookie_options); + setcookie("auth_pass_b64", base64_encode($password), $cookie_options); - $redir = json_decode(base64_decode($_GET["state"]), true)["redir"] ?? "/"; + $redir = safe_redir($state["redir"] ?? "/"); header("Location: $redir"); die(); @@ -89,6 +123,10 @@ if ($_GET["google"] == "1") { } $url = "https://accounts.google.com/o/oauth2/auth"; + // Generate a CSRF nonce and store it in the session + $oauth_nonce = bin2hex(random_bytes(16)); + $_SESSION["oauth_nonce"] = $oauth_nonce; + // build the HTTP GET query $params = array( "response_type" => "code", @@ -96,7 +134,8 @@ if ($_GET["google"] == "1") { "redirect_uri" => "https://$DOMAIN/_login.php?google_callback=1", "scope" => "email openid profile", "state" => base64_encode(json_encode([ - "redir" => $_GET["redir"] ?? "/" + "redir" => safe_redir($_GET["redir"] ?? "/"), + "nonce" => $oauth_nonce ])) ); @@ -107,16 +146,17 @@ if ($_GET["google"] == "1") { die(); } if ($_GET["logout"] == "1") { - $redir = $_GET["redir"] ?? "/"; - setcookie("auth_user", "", time() - 3600, "/"); - setcookie("auth_pass_b64", "", time() - 3600, "/"); + $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); session_destroy(); header("Location: $redir"); die(); } if ($_GET["clear_session"] == "1") { session_destroy(); - $redir = $_GET["redir"] ?? "/"; + $redir = safe_redir($_GET["redir"] ?? "/"); header("Location: $redir"); die(); } @@ -124,19 +164,22 @@ if (isset($_POST["user"])) { $valid = ""; $user = trim(strtolower($_POST["user"])); $password = $_POST["password"]; - $userdata = json_decode(file_get_contents("/DATA/Usuarios/" . Sf($user) . ".json"), true); + $user_filename = safe_username_to_filename($user); + $userdata = ($user_filename !== "") ? json_decode(@file_get_contents("/DATA/Usuarios/" . $user_filename . ".json"), true) : null; if (!isset($userdata["password_hash"])) { $_GET["_result"] = "El usuario no existe."; } - $hash = $userdata["password_hash"]; - if (password_verify($password, $hash)) { + $hash = $userdata["password_hash"] ?? null; + if ($hash && password_verify($password, $hash)) { + session_regenerate_id(true); $_SESSION['auth_user'] = $user; $_SESSION['auth_data'] = $userdata; $_SESSION['auth_ok'] = true; - setcookie("auth_user", $user, time() + (86400 * 30), "/"); - setcookie("auth_pass_b64", base64_encode($password), time() + (86400 * 30), "/"); - $redir = $_GET["redir"] ?? "/"; + $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); + $redir = safe_redir($_GET["redir"] ?? "/"); header("Location: $redir"); die(); } else { diff --git a/public_html/entreaulas/_filefetch.php b/public_html/entreaulas/_filefetch.php index c3df766..e181d88 100755 --- a/public_html/entreaulas/_filefetch.php +++ b/public_html/entreaulas/_filefetch.php @@ -1,8 +1,11 @@
@@ -461,8 +459,8 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") { } if ($action === "share_project") { - $project_id = Sf($_POST["project_id"] ?? ""); - $target_aulario = safe_path_segment(Sf($_POST["target_aulario"] ?? "")); + $project_id = safe_path_segment($_POST["project_id"] ?? ""); + $target_aulario = safe_path_segment($_POST["target_aulario"] ?? ""); if ($project_id !== "" && $target_aulario !== "" && $target_aulario !== $aulario_id) { // Only allow sharing local projects @@ -512,7 +510,7 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") { if (in_array("entreaulas:proyectos:delete", $_SESSION["auth_data"]["permissions"] ?? []) === false) { $error = "No tienes permisos para borrar proyectos."; } else { - $project_id = Sf($_POST["project_id"] ?? ""); + $project_id = safe_path_segment($_POST["project_id"] ?? ""); if ($project_id !== "") { $project = load_project($proyectos_dir, $project_id); $project_dir = find_project_path($proyectos_dir, $project_id); @@ -538,7 +536,7 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") { } if ($action === "edit_project") { - $project_id = Sf($_POST["project_id"] ?? ""); + $project_id = safe_path_segment($_POST["project_id"] ?? ""); $name = trim($_POST["name"] ?? ""); $description = sanitize_html($_POST["description"] ?? ""); @@ -559,15 +557,15 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") { } if ($action === "add_item") { - $project_id = Sf($_POST["project_id"] ?? ""); - $item_type = Sf($_POST["item_type"] ?? "link"); - $item_name = trim(Sf($_POST["item_name"] ?? "")); - $item_url = trim(Sf($_POST["item_url"] ?? "")); - $item_content = sanitize_html(Sf($_POST["item_content"] ?? "")); - $videocall_platform = Sf($_POST["videocall_platform"] ?? "jitsi"); - $videocall_room = trim(Sf($_POST["videocall_room"] ?? "")); - $videocall_url = trim(Sf($_POST["videocall_url"] ?? "")); - $source_aulario_param = Sf($_POST["source_aulario"] ?? ""); + $project_id = safe_path_segment($_POST["project_id"] ?? ""); + $item_type = safe_path_segment($_POST["item_type"] ?? "link"); + $item_name = trim($_POST["item_name"] ?? ""); + $item_url = trim($_POST["item_url"] ?? ""); + $item_content = sanitize_html($_POST["item_content"] ?? ""); + $videocall_platform = safe_path_segment($_POST["videocall_platform"] ?? "jitsi"); + $videocall_room = trim($_POST["videocall_room"] ?? ""); + $videocall_url = trim($_POST["videocall_url"] ?? ""); + $source_aulario_param = safe_path_segment($_POST["source_aulario"] ?? ""); // Determine which directory to use and permission level $working_dir = $proyectos_dir; @@ -579,10 +577,10 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") { // Validate the link $linked_projects = $aulario["linked_projects"] ?? []; foreach ($linked_projects as $link) { - if ((Sf($link["source_aulario"] ?? "") === $source_aulario_param) && - (Sf($link["project_id"] ?? "") === $project_id) + if ((($link["source_aulario"] ?? "") === $source_aulario_param) && + (($link["project_id"] ?? "") === $project_id) ) { - $permission = Sf($link["permission"] ?? "read_only"); + $permission = safe_path_segment($link["permission"] ?? "read_only"); if ($permission === "full_edit") { $working_dir = $proyectos_dir; } elseif ($permission === "request_edit") { @@ -777,8 +775,8 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") { } if ($action === "approve_change" || $action === "reject_change") { - $change_id = safe_filename(Sf($_POST["change_id"] ?? "")); - $project_id = Sf($_POST["project_id"] ?? ""); + $change_id = safe_filename($_POST["change_id"] ?? ""); + $project_id = safe_path_segment($_POST["project_id"] ?? ""); if (!empty($change_id) && !empty($project_id)) { $project_dir = find_project_path($proyectos_dir, $project_id); @@ -841,7 +839,7 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") { } if (in_array(($item["type"] ?? ""), ["file", "pdf_secure"], true) && !empty($change_data["pending_filename"])) { - $pending_filename = safe_filename(Sf($change_data["pending_filename"])); + $pending_filename = safe_filename($change_data["pending_filename"]); $pending_file = safe_join_file($pending_dir, $pending_filename); $target_file = safe_join_file($project_dir, $pending_filename); if ($pending_file && $target_file && file_exists($pending_file)) { @@ -850,7 +848,7 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") { } rename($pending_file, $target_file); if (!empty($item["filename"])) { - $old_path = safe_join_file($project_dir, Sf($item["filename"])); + $old_path = safe_join_file($project_dir, $item["filename"]); if ($old_path && file_exists($old_path)) { unlink($old_path); if (file_exists($old_path . ".eadat")) { @@ -859,7 +857,7 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") { } } $item["filename"] = $pending_filename; - $item["original_name"] = Sf($change_data["original_filename"] ?? $pending_filename); + $item["original_name"] = safe_filename($change_data["original_filename"] ?? $pending_filename); $file_meta = [ "id" => $item_id, @@ -907,7 +905,7 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") { $item["room"] = $change_data["item_room"] ?? ""; } elseif (in_array($change_data["item_type"], ["file", "pdf_secure"], true) && !empty($change_data["pending_filename"])) { // Move file from pending to project directory - $pending_filename = safe_filename(Sf($change_data["pending_filename"])); + $pending_filename = safe_filename($change_data["pending_filename"]); $pending_file = safe_join_file($pending_dir, $pending_filename); $target_file = safe_join_file($project_dir, $pending_filename); @@ -917,12 +915,12 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") { } rename($pending_file, $target_file); $item["filename"] = $pending_filename; - $item["original_name"] = Sf($change_data["original_filename"] ?? $pending_filename); + $item["original_name"] = safe_filename($change_data["original_filename"] ?? $pending_filename); $file_meta = [ "id" => $item_id, - "name" => Sf($change_data["item_name"]), - "type" => Sf($change_data["item_type"]), + "name" => $change_data["item_name"], + "type" => $change_data["item_type"], "original_name" => $item["original_name"], "created_at" => $item["created_at"] ]; @@ -943,7 +941,7 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") { } else { // Reject - just delete pending file if exists if (!empty($change_data["pending_filename"])) { - $pending_file = safe_join_file($pending_dir, Sf($change_data["pending_filename"])); + $pending_file = safe_join_file($pending_dir, safe_filename($change_data["pending_filename"])); if ($pending_file && file_exists($pending_file)) { unlink($pending_file); } @@ -960,9 +958,9 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") { } if ($action === "delete_item") { - $project_id = Sf($_POST["project_id"] ?? ""); - $item_id = Sf($_POST["item_id"] ?? ""); - $source_aulario_param = Sf($_POST["source_aulario"] ?? ""); + $project_id = safe_path_segment($_POST["project_id"] ?? ""); + $item_id = safe_path_segment($_POST["item_id"] ?? ""); + $source_aulario_param = safe_path_segment($_POST["source_aulario"] ?? ""); // Determine which directory to use based on whether this is a linked project $working_dir = $proyectos_dir; @@ -1069,15 +1067,15 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") { } if ($action === "edit_item") { - $project_id = Sf($_POST["project_id"] ?? ""); - $item_id = Sf($_POST["item_id"] ?? ""); - $item_name = Sf(trim($_POST["item_name"] ?? "")); - $item_url = Sf(trim($_POST["item_url"] ?? "")); - $item_content = Sf(sanitize_html($_POST["item_content"] ?? "")); - $videocall_platform = Sf($_POST["edit_videocall_platform"] ?? "jitsi"); - $videocall_room = Sf(trim($_POST["edit_videocall_room"] ?? "")); - $videocall_url = Sf(trim($_POST["edit_videocall_url"] ?? "")); - $source_aulario_param = Sf($_POST["source_aulario"] ?? ""); + $project_id = safe_path_segment($_POST["project_id"] ?? ""); + $item_id = safe_path_segment($_POST["item_id"] ?? ""); + $item_name = trim($_POST["item_name"] ?? ""); + $item_url = trim($_POST["item_url"] ?? ""); + $item_content = sanitize_html($_POST["item_content"] ?? ""); + $videocall_platform = safe_path_segment($_POST["edit_videocall_platform"] ?? "jitsi"); + $videocall_room = trim($_POST["edit_videocall_room"] ?? ""); + $videocall_url = trim($_POST["edit_videocall_url"] ?? ""); + $source_aulario_param = safe_path_segment($_POST["source_aulario"] ?? ""); $working_dir = $proyectos_dir; $permission = "full_edit"; diff --git a/public_html/sysadmin/reset_password.php b/public_html/sysadmin/reset_password.php index 269f0c5..8453334 100644 --- a/public_html/sysadmin/reset_password.php +++ b/public_html/sysadmin/reset_password.php @@ -14,7 +14,7 @@ function safe_username($value) switch ($_GET['form'] ?? '') { case 'save_password': - $username = safe_username(Sf($_POST['username'] ?? '')); + $username = safe_username($_POST['username'] ?? ''); $new_password = $_POST['new_password'] ?? ''; $confirm_password = $_POST['confirm_password'] ?? ''; diff --git a/public_html/sysadmin/users.php b/public_html/sysadmin/users.php index c044f23..7503e12 100644 --- a/public_html/sysadmin/users.php +++ b/public_html/sysadmin/users.php @@ -32,7 +32,7 @@ function safe_aulario_id($value) switch ($_GET['form'] ?? '') { case 'save_edit': - $username = safe_username(Sf($_POST['username'] ?? '')); + $username = safe_username($_POST['username'] ?? ''); if (empty($username)) { die("Nombre de usuario no proporcionado."); } @@ -175,7 +175,7 @@ switch ($_GET['action'] ?? '') { break; case 'edit': require_once "_incl/pre-body.php"; - $username = safe_username(Sf($_GET['user'] ?? '')); + $username = safe_username($_GET['user'] ?? ''); if (empty($username)) { die("Nombre de usuario inválido."); } From 56918315ea410fcf5330dc87e5cace50bd969d2c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Feb 2026 18:56:35 +0000 Subject: [PATCH 3/3] Security: improve login auth check to use if/elseif structure for clarity Remove the intermediate variable pattern that could potentially allow authentication without a valid password_hash, using an if/elseif pattern instead. Co-authored-by: naielv <109038805+naielv@users.noreply.github.com> --- public_html/_login.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/public_html/_login.php b/public_html/_login.php index c459471..324ac4e 100755 --- a/public_html/_login.php +++ b/public_html/_login.php @@ -166,12 +166,9 @@ if (isset($_POST["user"])) { $password = $_POST["password"]; $user_filename = safe_username_to_filename($user); $userdata = ($user_filename !== "") ? json_decode(@file_get_contents("/DATA/Usuarios/" . $user_filename . ".json"), true) : null; - if (!isset($userdata["password_hash"])) { + if (!is_array($userdata) || !isset($userdata["password_hash"])) { $_GET["_result"] = "El usuario no existe."; - } - - $hash = $userdata["password_hash"] ?? null; - if ($hash && password_verify($password, $hash)) { + } elseif (password_verify($password, $userdata["password_hash"])) { session_regenerate_id(true); $_SESSION['auth_user'] = $user; $_SESSION['auth_data'] = $userdata;