diff --git a/public_html/entreaulas/_filefetch.php b/public_html/entreaulas/_filefetch.php index 57af825..c3df766 100755 --- a/public_html/entreaulas/_filefetch.php +++ b/public_html/entreaulas/_filefetch.php @@ -5,11 +5,31 @@ ob_end_flush(); ini_set('memory_limit', '1G'); header("Access-Control-Allow-Origin: *"); -switch ($_GET["type"]) { +function safe_id_segment($value) +{ + $value = basename((string)$value); + return preg_replace('/[^A-Za-z0-9_-]/', '', $value); +} + +function safe_centro_id($value) +{ + return preg_replace('/[^0-9]/', '', (string)$value); +} + +function safe_filename($name) +{ + $name = basename((string)$name); + $name = preg_replace('/[^A-Za-z0-9._-]/', '_', $name); + $name = ltrim($name, '.'); + return $name; +} + +$type = $_GET["type"] ?? ""; +switch ($type) { case "alumno_photo": - $centro = basename($_GET["centro"] ?? ''); - $aulario = basename($_GET["aulario"] ?? ''); - $alumno = basename($_GET["alumno"] ?? ''); + $centro = safe_centro_id($_GET["centro"] ?? ''); + $aulario = safe_id_segment($_GET["aulario"] ?? ''); + $alumno = safe_id_segment($_GET["alumno"] ?? ''); // Additional validation to prevent empty names if (empty($centro) || empty($aulario) || empty($alumno)) { header("HTTP/1.1 403 Forbidden"); @@ -18,15 +38,23 @@ switch ($_GET["type"]) { $relpath = "entreaulas/Centros/$centro/Aularios/$aulario/Alumnos/$alumno/photo.jpg"; break; case "panel_actividades": - $centro = str_replace('..', '_', $_GET["centro"] ?? ''); - $activity = str_replace('..', '_', $_GET["activity"] ?? ''); + $centro = safe_centro_id($_GET["centro"] ?? ''); + $activity = safe_id_segment($_GET["activity"] ?? ''); + if (empty($centro) || empty($activity)) { + header("HTTP/1.1 400 Bad Request"); + die("Invalid parameters"); + } $relpath = "entreaulas/Centros/$centro/Panel/Actividades/$activity/photo.jpg"; break; case "comedor_image": - $centro = str_replace('..', '_', $_GET["centro"] ?? ''); - $aulario = str_replace('..', '_', $_GET["aulario"] ?? ''); + $centro = safe_centro_id($_GET["centro"] ?? ''); + $aulario = safe_id_segment($_GET["aulario"] ?? ''); $date = preg_replace('/[^0-9-]/', '', $_GET["date"] ?? ''); - $file = basename($_GET["file"] ?? ''); + $file = safe_filename($_GET["file"] ?? ''); + if (empty($centro) || empty($aulario) || empty($file)) { + header("HTTP/1.1 400 Bad Request"); + die("Invalid parameters"); + } if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) { header("HTTP/1.1 400 Bad Request"); die("Invalid date"); @@ -36,9 +64,13 @@ switch ($_GET["type"]) { $relpath = "entreaulas/Centros/$centro/Aularios/$aulario/Comedor/$ym/$day/$file"; break; case "proyecto_file": - $centro = str_replace('..', '_', $_GET["centro"] ?? ''); - $project = str_replace('..', '_', $_GET["project"] ?? ''); - $file = basename($_GET["file"] ?? ''); + $centro = safe_centro_id($_GET["centro"] ?? ''); + $project = safe_id_segment($_GET["project"] ?? ''); + $file = safe_filename($_GET["file"] ?? ''); + if (empty($centro) || empty($project) || empty($file)) { + header("HTTP/1.1 400 Bad Request"); + die("Invalid parameters"); + } // Ensure no directory traversal if (strpos($file, '..') !== false || strpos($file, '/') !== false || strpos($file, '\\') !== false) { header("HTTP/1.1 400 Bad Request"); @@ -73,6 +105,9 @@ switch ($_GET["type"]) { $path = $project_dir . "/" . $file; $uripath = str_replace("/DATA", "", $path); break; + default: + header("HTTP/1.1 400 Bad Request"); + die("Invalid type"); } if (!isset($path)) { $path = "/DATA/$relpath"; @@ -84,7 +119,8 @@ if (!isset($uripath)) { // Validate that the resolved path is within /DATA directory $real_path = realpath($path); $real_base = realpath("/DATA"); -if ($real_path === false || $real_base === false || strpos($real_path, $real_base) !== 0) { +$real_base_prefix = $real_base !== false ? rtrim($real_base, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : null; +if ($real_path === false || $real_base === false || $real_base_prefix === null || strpos($real_path, $real_base_prefix) !== 0) { header("HTTP/1.1 403 Forbidden"); die("Access denied"); } @@ -96,7 +132,7 @@ if (!file_exists($real_path) || !is_file($real_path)) { $mime = mime_content_type($real_path); // Check if thumbnail is requested -if (file_exists($real_path . ".thumbnail") && $_GET["thumbnail"] == "1") { +if (file_exists($real_path . ".thumbnail") && (($_GET["thumbnail"] ?? "") === "1")) { $real_path .= ".thumbnail"; $uripath .= ".thumbnail"; $mime = "image/jpeg"; diff --git a/public_html/entreaulas/alumnos.php b/public_html/entreaulas/alumnos.php index 70035b5..c47b9ad 100644 --- a/public_html/entreaulas/alumnos.php +++ b/public_html/entreaulas/alumnos.php @@ -7,8 +7,37 @@ if (!in_array("entreaulas:docente", $_SESSION["auth_data"]["permissions"] ?? []) die("Access denied"); } -$aulario_id = $_GET["aulario"] ?? ""; -$centro_id = $_SESSION["auth_data"]["entreaulas"]["centro"] ?? ""; +function safe_id_segment($value) +{ + $value = basename((string)$value); + return preg_replace('/[^A-Za-z0-9_-]/', '', $value); +} + +function safe_centro_id($value) +{ + return preg_replace('/[^0-9]/', '', (string)$value); +} + +function safe_alumno_name($value) +{ + $value = basename((string)$value); + $value = trim($value); + $value = preg_replace('/[\x00-\x1F\x7F]/u', '', $value); + $value = str_replace(['/', '\\'], '', $value); + return $value; +} + +function path_is_within($real_base, $real_path) +{ + if ($real_base === false || $real_path === false) { + return false; + } + $base_prefix = rtrim($real_base, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; + return strpos($real_path, $base_prefix) === 0 || $real_path === rtrim($real_base, DIRECTORY_SEPARATOR); +} + +$aulario_id = safe_id_segment($_GET["aulario"] ?? ""); +$centro_id = safe_centro_id($_SESSION["auth_data"]["entreaulas"]["centro"] ?? ""); if (empty($aulario_id) || empty($centro_id)) { require_once "_incl/pre-body.php"; @@ -22,9 +51,6 @@ if (empty($aulario_id) || empty($centro_id)) { exit; } -$aulario_id = basename($aulario_id); -$centro_id = basename($centro_id); - // Validate paths with realpath $base_path = "/DATA/entreaulas/Centros"; $real_base = realpath($base_path); @@ -53,7 +79,11 @@ switch ($_GET["form"] ?? '') { } // Sanitize filename - $nombre_safe = basename($nombre); + $nombre_safe = safe_alumno_name($nombre); + if ($nombre_safe === "") { + header("Location: ?aulario=" . urlencode($aulario_id) . "&_result=" . urlencode("Nombre inválido")); + exit; + } $alumno_path = "$alumnos_base_path/$nombre_safe"; // Validate path with realpath (after potential creation) @@ -62,7 +92,7 @@ switch ($_GET["form"] ?? '') { } $real_alumnos_base = realpath($alumnos_base_path); - if ($real_alumnos_base === false || strpos($real_alumnos_base, $real_base) !== 0) { + if (!path_is_within($real_base, $real_alumnos_base)) { header("Location: ?aulario=" . urlencode($aulario_id) . "&_result=" . urlencode("Error: Ruta inválida")); exit; } @@ -92,7 +122,7 @@ switch ($_GET["form"] ?? '') { exit; case 'edit': - $nombre_old = basename($_POST['nombre_old'] ?? ''); + $nombre_old = safe_alumno_name($_POST['nombre_old'] ?? ''); $nombre_new = trim($_POST['nombre_new'] ?? ''); if (empty($nombre_old) || empty($nombre_new)) { @@ -100,13 +130,27 @@ switch ($_GET["form"] ?? '') { exit; } - $nombre_new_safe = basename($nombre_new); + $nombre_new_safe = safe_alumno_name($nombre_new); + if ($nombre_new_safe === "") { + header("Location: ?aulario=" . urlencode($aulario_id) . "&_result=" . urlencode("Nombre inválido")); + exit; + } $alumno_old_path = "$alumnos_base_path/$nombre_old"; $alumno_new_path = "$alumnos_base_path/$nombre_new_safe"; + + if (!is_dir($alumnos_base_path)) { + header("Location: ?aulario=" . urlencode($aulario_id) . "&_result=" . urlencode("Alumno no encontrado")); + exit; + } + $real_alumnos_base = realpath($alumnos_base_path); + if (!path_is_within($real_base, $real_alumnos_base)) { + header("Location: ?aulario=" . urlencode($aulario_id) . "&_result=" . urlencode("Ruta inválida")); + exit; + } // Validate paths with realpath $real_old_path = realpath($alumno_old_path); - if ($real_old_path === false || strpos($real_old_path, $real_base) !== 0) { + if (!path_is_within($real_alumnos_base, $real_old_path)) { header("Location: ?aulario=" . urlencode($aulario_id) . "&_result=" . urlencode("Alumno no encontrado")); exit; } @@ -144,17 +188,27 @@ switch ($_GET["form"] ?? '') { exit; case 'delete': - $nombre = basename($_POST['nombre'] ?? ''); + $nombre = safe_alumno_name($_POST['nombre'] ?? ''); if (empty($nombre)) { header("Location: ?aulario=" . urlencode($aulario_id) . "&_result=" . urlencode("Nombre inválido")); exit; } $alumno_path = "$alumnos_base_path/$nombre"; + + if (!is_dir($alumnos_base_path)) { + header("Location: ?aulario=" . urlencode($aulario_id) . "&_result=" . urlencode("Alumno no encontrado")); + exit; + } + $real_alumnos_base = realpath($alumnos_base_path); + if (!path_is_within($real_base, $real_alumnos_base)) { + header("Location: ?aulario=" . urlencode($aulario_id) . "&_result=" . urlencode("Ruta inválida")); + exit; + } // Validate path with realpath $real_alumno_path = realpath($alumno_path); - if ($real_alumno_path === false || strpos($real_alumno_path, $real_base) !== 0) { + if (!path_is_within($real_alumnos_base, $real_alumno_path)) { header("Location: ?aulario=" . urlencode($aulario_id) . "&_result=" . urlencode("Alumno no encontrado")); exit; } @@ -198,7 +252,7 @@ switch ($_GET["action"] ?? '') { exit; case 'edit': - $nombre = basename($_GET['alumno'] ?? ''); + $nombre = safe_alumno_name($_GET['alumno'] ?? ''); $alumno_path = "$alumnos_base_path/$nombre"; if (empty($nombre) || !file_exists($alumno_path)) { @@ -246,7 +300,7 @@ switch ($_GET["action"] ?? '') { exit; case 'delete': - $nombre = basename($_GET['alumno'] ?? ''); + $nombre = safe_alumno_name($_GET['alumno'] ?? ''); $alumno_path = "$alumnos_base_path/$nombre"; if (empty($nombre) || !file_exists($alumno_path)) { diff --git a/public_html/entreaulas/api/comedor.php b/public_html/entreaulas/api/comedor.php index 6634234..10b0a91 100644 --- a/public_html/entreaulas/api/comedor.php +++ b/public_html/entreaulas/api/comedor.php @@ -3,20 +3,56 @@ header("Content-Type: application/json; charset=utf-8"); require_once __DIR__ . "/../_incl/tools.security.php"; require_once __DIR__ . "/../_incl/auth_redir.php"; +function safe_id_segment($value) { + $value = basename((string)$value); + return preg_replace('/[^A-Za-z0-9_-]/', '', $value); +} + +function safe_centro_id($value) { + return preg_replace('/[^0-9]/', '', (string)$value); +} + +function safe_aulario_config_path($centro_id, $aulario_id) { + $centro = safe_centro_id($centro_id); + $aulario = safe_id_segment($aulario_id); + if ($centro === '' || $aulario === '') { + return null; + } + return "/DATA/entreaulas/Centros/$centro/Aularios/$aulario.json"; +} + +function menu_types_path($centro_id, $aulario_id) { + $centro = safe_centro_id($centro_id); + $aulario = safe_id_segment($aulario_id); + if ($centro === '' || $aulario === '') { + return null; + } + return "/DATA/entreaulas/Centros/$centro/Aularios/$aulario/Comedor-MenuTypes.json"; +} + +function comedor_day_base_dir($centro_id, $aulario_id, $ym, $day) { + $centro = safe_centro_id($centro_id); + $aulario = safe_id_segment($aulario_id); + if ($centro === '' || $aulario === '') { + return null; + } + return "/DATA/entreaulas/Centros/$centro/Aularios/$aulario/Comedor/$ym/$day"; +} + // Check permissions if (!in_array("entreaulas:docente", $_SESSION["auth_data"]["permissions"] ?? [])) { http_response_code(403); die(json_encode(["error" => "Access denied", "code" => "FORBIDDEN"])); } -$centro_id = $_SESSION["auth_data"]["entreaulas"]["centro"] ?? ""; +$centro_id = safe_centro_id($_SESSION["auth_data"]["entreaulas"]["centro"] ?? ""); if ($centro_id === "") { http_response_code(400); die(json_encode(["error" => "Centro not found in session", "code" => "INVALID_SESSION"])); } $action = $_GET["action"] ?? ($_POST["action"] ?? ""); -$aulario_id = Sf($_GET["aulario"] ?? $_POST["aulario"] ?? ""); +$aulario_id = safe_id_segment(Sf($_GET["aulario"] ?? $_POST["aulario"] ?? "")); // Validate aulario_id if ($aulario_id === "") { @@ -26,22 +62,23 @@ if ($aulario_id === "") { // Verify that the user has access to this aulario $userAulas = $_SESSION["auth_data"]["entreaulas"]["aulas"] ?? []; -if (!in_array($aulario_id, $userAulas)) { +$userAulas = array_values(array_filter(array_map('safe_id_segment', $userAulas))); +if (!in_array($aulario_id, $userAulas, true)) { http_response_code(403); die(json_encode(["error" => "Access denied to this aulario", "code" => "FORBIDDEN"])); } -$aulario_path = "/DATA/entreaulas/Centros/$centro_id/Aularios/$aulario_id.json"; -$aulario = file_exists($aulario_path) ? json_decode(file_get_contents($aulario_path), true) : null; +$aulario_path = safe_aulario_config_path($centro_id, $aulario_id); +$aulario = ($aulario_path && file_exists($aulario_path)) ? json_decode(file_get_contents($aulario_path), true) : null; // Handle shared comedor data $source_aulario_id = $aulario_id; $is_shared = false; if ($aulario && !empty($aulario["shared_comedor_from"])) { - $shared_from = $aulario["shared_comedor_from"]; - $shared_aulario_path = "/DATA/entreaulas/Centros/$centro_id/Aularios/$shared_from.json"; - if (file_exists($shared_aulario_path)) { - $source_aulario_id = Sf($shared_from); + $shared_from = safe_id_segment($aulario["shared_comedor_from"]); + $shared_aulario_path = safe_aulario_config_path($centro_id, $shared_from); + if ($shared_aulario_path && file_exists($shared_aulario_path)) { + $source_aulario_id = $shared_from; $is_shared = true; } } @@ -51,12 +88,16 @@ $canEdit = in_array("sysadmin:access", $_SESSION["auth_data"]["permissions"] ?? // Helper functions function get_menu_types($centro_id, $source_aulario_id) { - $menuTypesPath = "/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_id/Comedor-MenuTypes.json"; + $menuTypesPath = menu_types_path($centro_id, $source_aulario_id); $defaultMenuTypes = [ ["id" => "basal", "label" => "Menú basal", "color" => "#0d6efd"], ["id" => "vegetariano", "label" => "Menú vegetariano", "color" => "#198754"], ["id" => "alergias", "label" => "Menú alergias", "color" => "#dc3545"], ]; + + if ($menuTypesPath === null) { + return $defaultMenuTypes; + } if (!file_exists($menuTypesPath)) { if (!is_dir(dirname($menuTypesPath))) { @@ -146,7 +187,7 @@ function handle_get_menu() { global $centro_id, $source_aulario_id; $date = $_GET["date"] ?? date("Y-m-d"); - $menuTypeId = $_GET["menu"] ?? ""; + $menuTypeId = safe_id_segment($_GET["menu"] ?? ""); // Validate date $dateObj = DateTime::createFromFormat("Y-m-d", $date); @@ -172,7 +213,11 @@ function handle_get_menu() { // Get menu data $ym = $dateObj->format("Y-m"); $day = $dateObj->format("d"); - $baseDir = "/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_id/Comedor/$ym/$day"; + $baseDir = comedor_day_base_dir($centro_id, $source_aulario_id, $ym, $day); + if ($baseDir === null) { + http_response_code(400); + die(json_encode(["error" => "Invalid path parameters", "code" => "INVALID_PATH"])); + } $dataPath = "$baseDir/_datos.json"; $menuData = [ @@ -212,7 +257,7 @@ function handle_save_menu() { } $date = $input["date"] ?? date("Y-m-d"); - $menuTypeId = $input["menu_type"] ?? ""; + $menuTypeId = safe_id_segment($input["menu_type"] ?? ""); $plates = $input["plates"] ?? []; // Validate date @@ -240,7 +285,11 @@ function handle_save_menu() { // Get existing menu data $ym = $dateObj->format("Y-m"); $day = $dateObj->format("d"); - $baseDir = "/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_id/Comedor/$ym/$day"; + $baseDir = comedor_day_base_dir($centro_id, $source_aulario_id, $ym, $day); + if ($baseDir === null) { + http_response_code(400); + die(json_encode(["error" => "Invalid path parameters", "code" => "INVALID_PATH"])); + } $dataPath = "$baseDir/_datos.json"; $menuData = [ @@ -292,7 +341,7 @@ function handle_add_menu_type() { $input = $_POST; } - $newId = strtolower(trim($input["id"] ?? "")); + $newId = safe_id_segment(strtolower(trim($input["id"] ?? ""))); $newLabel = trim($input["label"] ?? ""); $newColor = trim($input["color"] ?? "#0d6efd"); @@ -301,7 +350,11 @@ function handle_add_menu_type() { die(json_encode(["error" => "id and label are required", "code" => "MISSING_PARAM"])); } - $menuTypesPath = "/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_id/Comedor-MenuTypes.json"; + $menuTypesPath = menu_types_path($centro_id, $source_aulario_id); + if ($menuTypesPath === null) { + http_response_code(400); + die(json_encode(["error" => "Invalid path parameters", "code" => "INVALID_PATH"])); + } $menuTypes = get_menu_types($centro_id, $source_aulario_id); // Check if already exists @@ -330,14 +383,18 @@ function handle_delete_menu_type() { $input = $_POST; } - $deleteId = trim($input["id"] ?? ""); + $deleteId = safe_id_segment(trim($input["id"] ?? "")); if ($deleteId === "") { http_response_code(400); die(json_encode(["error" => "id is required", "code" => "MISSING_PARAM"])); } - $menuTypesPath = "/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_id/Comedor-MenuTypes.json"; + $menuTypesPath = menu_types_path($centro_id, $source_aulario_id); + if ($menuTypesPath === null) { + http_response_code(400); + die(json_encode(["error" => "Invalid path parameters", "code" => "INVALID_PATH"])); + } $menuTypes = get_menu_types($centro_id, $source_aulario_id); $deleted = false; @@ -371,7 +428,7 @@ function handle_rename_menu_type() { $input = $_POST; } - $renameId = trim($input["id"] ?? ""); + $renameId = safe_id_segment(trim($input["id"] ?? "")); $newLabel = trim($input["label"] ?? ""); $newColor = trim($input["color"] ?? ""); @@ -380,7 +437,11 @@ function handle_rename_menu_type() { die(json_encode(["error" => "id and label are required", "code" => "MISSING_PARAM"])); } - $menuTypesPath = "/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_id/Comedor-MenuTypes.json"; + $menuTypesPath = menu_types_path($centro_id, $source_aulario_id); + if ($menuTypesPath === null) { + http_response_code(400); + die(json_encode(["error" => "Invalid path parameters", "code" => "INVALID_PATH"])); + } $menuTypes = get_menu_types($centro_id, $source_aulario_id); $found = false; diff --git a/public_html/entreaulas/aulario.php b/public_html/entreaulas/aulario.php index 238acd4..f7a3a24 100644 --- a/public_html/entreaulas/aulario.php +++ b/public_html/entreaulas/aulario.php @@ -3,9 +3,39 @@ require_once "_incl/auth_redir.php"; require_once "_incl/pre-body.php"; require_once "_incl/tools.security.php"; -$aulario_id = Sf($_GET["id"]); -$centro_id = $_SESSION["auth_data"]["entreaulas"]["centro"]; -$aulario = json_decode(file_get_contents("/DATA/entreaulas/Centros/$centro_id/Aularios/$aulario_id.json"), true); +function safe_id_segment($value) +{ + $value = basename((string)$value); + return preg_replace('/[^A-Za-z0-9_-]/', '', $value); +} + +function safe_centro_id($value) +{ + return preg_replace('/[^0-9]/', '', (string)$value); +} + +function safe_aulario_config_path($centro_id, $aulario_id) +{ + $centro = safe_centro_id($centro_id); + $aulario = safe_id_segment($aulario_id); + if ($centro === '' || $aulario === '') { + return null; + } + return "/DATA/entreaulas/Centros/$centro/Aularios/$aulario.json"; +} + +$aulario_id = safe_id_segment(Sf($_GET["id"] ?? "")); +$centro_id = safe_centro_id($_SESSION["auth_data"]["entreaulas"]["centro"] ?? ""); +$aulario_path = safe_aulario_config_path($centro_id, $aulario_id); +$aulario = ($aulario_path && file_exists($aulario_path)) ? json_decode(file_get_contents($aulario_path), true) : null; + +if (!$aulario || !is_array($aulario)) { +?> +
No se ha podido cargar la configuración del aulario.
+Ruta de alumnos inválida.
+