sessionname); session_start(); } if (empty($_SESSION['usersession']) || empty($_SESSION['isadmin'])) { while (ob_get_level() > 0) { @ob_end_clean(); } http_response_code(403); header('Content-Type: application/json; charset=utf-8'); echo json_encode(array('code' => '403', 'msg' => 'No autorizado'), JSON_UNESCAPED_UNICODE); exit; } // Cualquier salida accidental (avisos/notices) antes del JSON se descarta aquí. while (ob_get_level() > 0) { @ob_end_clean(); } ob_start(); // Excepciones no controladas (p. ej. mysqli_sql_exception en PHP 8+) deben // responder JSON, nunca HTML con stack trace (evita el "Unexpected token '<'"). set_exception_handler(function ($e) { while (ob_get_level() > 0) { @ob_end_clean(); } error_log('admin endpoint: ' . $e->getMessage()); http_response_code(500); header('Content-Type: application/json; charset=utf-8'); echo json_encode(array('code' => '500', 'msg' => 'Error interno del servidor', 'error' => $e->getMessage()), JSON_UNESCAPED_UNICODE); }); if (empty($_SESSION['csrf'])) { $_SESSION['csrf'] = bin2hex(random_bytes(32)); } // Verificación CSRF en métodos que cambian estado $metodo = isset($_SERVER['REQUEST_METHOD']) ? strtoupper($_SERVER['REQUEST_METHOD']) : 'GET'; if ($metodo === 'POST' || $metodo === 'PUT' || $metodo === 'DELETE') { $token = isset($_SERVER['HTTP_X_CSRF_TOKEN']) ? $_SERVER['HTTP_X_CSRF_TOKEN'] : ''; if ($token === '' && isset($_POST['csrf'])) { $token = $_POST['csrf']; } if (!is_string($token) || !hash_equals($_SESSION['csrf'], $token)) { http_response_code(403); header('Content-Type: application/json; charset=utf-8'); echo json_encode(array('code' => '403', 'msg' => 'Token CSRF inválido'), JSON_UNESCAPED_UNICODE); exit; } }