Cómo crear un panel de administración web para MU Online
Construye un panel de administración web seguro para tu servidor de MU Online, con autenticación fuerte, control de permisos, gestión de cuentas y personajes, baneos, envío de ítems y logs de auditoría completos.
Llega un momento en la vida de todo servidor de MU Online en que abrir el Navicat o el SQL Server Management Studio para cada baneo, cada devolución de ítem y cada reset manual deja de ser sostenible. Es lento, es peligroso, no deja rastro de quién hizo qué, y es impensable dar ese tipo de acceso a
Llega un momento en la vida de todo servidor de MU Online en que abrir el Navicat o el SQL Server Management Studio para cada baneo, cada devolución de ítem y cada reset manual deja de ser sostenible. Es lento, es peligroso, no deja rastro de quién hizo qué, y es impensable dar ese tipo de acceso a un GM voluntario. La solución profesional es un panel de administración web: una aplicación que expone solo las operaciones necesarias, con validación, control de permisos y auditoría completa. En este tutorial avanzado vas a diseñar y construir ese panel desde cero, con atención especial a la parte que más gente ignora y que más estragos causa — la seguridad.
Un panel de administración es, por definición, el objetivo más valioso de tu servidor. Quien lo compromete puede dar ítems infinitos, banear jugadores legítimos, robar cuentas y destruir la economía en minutos. Por eso, cada sección de esta guía trata el panel como un sistema hostil por naturaleza: asume que será atacado y diséñalo para resistir. Todos los nombres de tabla, columna y estructura son ejemplos típicos y varían por versión (Season 6, IGCN, MuEMU, Season 16+); confirma el schema real de tu distribución antes de ejecutar cualquier comando.
Requisitos previos
- Entorno con PHP 8.1+ y PDO (driver
sqlsrvpara SQL Server) o stack equivalente (Node.js, .NET). - Acceso a la base del juego (SQL Server, común en MU) y, opcionalmente, a la base del sitio.
- HTTPS válido en el dominio del panel (Let's Encrypt).
- Servidor web configurado para servir el panel en una ruta separada y protegida (subdominio o carpeta con reglas propias).
- Conocimiento de SQL parametrizado, sesiones y hashing de contraseñas.
- Un plan de roles: quién es Admin, quién es GM, qué puede hacer cada uno.
> Regla de oro: la base de datos del juego nunca debe estar accesible directamente desde internet. El panel es el único puente, y necesita estar blindado.
Arquitectura y capas de seguridad
Piensa en el panel como una cebolla de capas. Un atacante necesita atravesar todas para causar daño. Cada capa es independiente de las demás.
- Red: el panel corre en un subdominio propio, detrás de HTTPS, idealmente con restricción de IP o VPN para los administradores.
- Autenticación: login con contraseña en hash fuerte (bcrypt/Argon2) y 2FA para los admins.
- Autorización: cada acción verifica el rol del usuario (RBAC). Un GM no puede acceder a rutas de admin.
- Validación: toda entrada es validada y todo SQL es parametrizado (nunca concatenado).
- Auditoría: cada acción sensible es registrada con quién, qué, cuándo y desde dónde.
| Capa | Amenaza que bloquea | Mecanismo |
|---|---|---|
| Red | Escaneo y acceso externo | HTTPS, restricción de IP, subdominio |
| Autenticación | Login indebido | Hash de contraseña, 2FA, rate limit |
| Autorización | Escalada de privilegios | RBAC por rol |
| Validación | SQL Injection, XSS | PDO parametrizado, escape de salida |
| Auditoría | Abuso interno / negación | Log inmutable de acciones |
Modelado de usuarios y roles del panel
El panel tiene sus propios usuarios, separados de las cuentas del juego. Un administrador del panel no es lo mismo que una cuenta de jugador. Crea en la base del sitio:
CREATE TABLE admin_users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(40) UNIQUE NOT NULL,
pass_hash VARCHAR(255) NOT NULL, -- bcrypt/argon2
role ENUM('admin','gm','support') NOT NULL DEFAULT 'support',
totp_secret VARCHAR(64) NULL, -- 2FA
last_ip VARCHAR(45) NULL,
last_login DATETIME NULL,
active TINYINT(1) DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
Y la tabla de auditoría, el elemento más importante de todo el panel:
CREATE TABLE admin_audit (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
admin_id INT NOT NULL,
action VARCHAR(60) NOT NULL, -- 'ban_account', 'give_item'...
target VARCHAR(60) NULL, -- cuenta/personaje objetivo
details TEXT NULL, -- JSON con antes/después
ip VARCHAR(45) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_admin (admin_id, created_at)
);
Autenticación fuerte y sesión
Nunca almacenes la contraseña en texto plano. Usa password_hash() con bcrypt o Argon2. En el login, verifica con password_verify() y regenera la sesión para evitar el fixation.
<?php
session_start();
function login(string $user, string $pass, PDO $db): bool {
$stmt = $db->prepare("SELECT id, pass_hash, role, active, totp_secret
FROM admin_users WHERE username = ?");
$stmt->execute([$user]);
$u = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$u || !$u['active'] || !password_verify($pass, $u['pass_hash'])) {
registrarTentativaFalha($user, $_SERVER['REMOTE_ADDR']);
return false;
}
// si hay 2FA, exigir el código TOTP antes de concluir
if ($u['totp_secret']) { $_SESSION['pending_2fa'] = $u['id']; return true; }
session_regenerate_id(true);
$_SESSION['admin_id'] = $u['id'];
$_SESSION['role'] = $u['role'];
return true;
}
Adopta 2FA (TOTP) para todos los administradores — es la diferencia entre que una filtración de contraseña sea un susto o una catástrofe. Aplica rate limiting en el login (ej.: bloquear tras 5 intentos en 10 minutos por IP) para contener la fuerza bruta.
Control de permisos (RBAC)
Cada ruta del panel declara qué rol puede acceder a ella. Centraliza esa verificación en un middleware para no olvidar ninguna ruta.
<?php
function exigirPapel(array $permitidos): void {
if (empty($_SESSION['admin_id']) || !in_array($_SESSION['role'], $permitidos, true)) {
http_response_code(403);
exit('Acceso denegado');
}
}
// al inicio de una ruta sensible:
exigirPapel(['admin']); // solo admin
// o
exigirPapel(['admin', 'gm']); // admin y gm
Una matriz de permisos deja claro quién hace qué. Ajústala según la confianza de tu equipo:
| Acción | Admin | GM | Soporte |
|---|---|---|---|
| Ver cuentas y personajes | Sí | Sí | Sí |
| Banear / desbanear | Sí | Sí | No |
| Dar ítems / zen | Sí | Sí | No |
| Editar resets / level | Sí | No | No |
| Gestionar usuarios del panel | Sí | No | No |
| Ver logs de auditoría | Sí | No | No |
Módulo de gestión de cuentas y personajes
El núcleo funcional del panel es consultar y editar cuentas. Siempre usa consultas parametrizadas — concatenar strings en SQL es la puerta principal al SQL Injection, y en un panel de admin eso es fatal.
<?php
// buscar cuenta con seguridad (los nombres de columna VARÍAN por versión)
function buscarConta(string $account, PDO $game): ?array {
$stmt = $game->prepare(
"SELECT memb___id, memb_name, mail_addr, bloc_code, ctl1_code
FROM MEMB_INFO WHERE memb___id = ?"
);
$stmt->execute([$account]);
return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
}
La tabla MEMB_INFO y las columnas bloc_code (bloqueo) y ctl1_code (tipo de cuenta/GM) son ejemplos típicos de Season 6. Tu versión puede tener nombres y estructuras diferentes — confirma siempre.
Módulo de baneo
Banear normalmente significa marcar la cuenta como bloqueada. Registra la acción en la auditoría y, si es posible, el motivo y la duración.
<?php
function banirConta(string $account, string $motivo, PDO $game, PDO $site): void {
exigirPapel(['admin', 'gm']);
// estado anterior para el log
$antes = buscarConta($account, $game);
$game->prepare("UPDATE MEMB_INFO SET bloc_code = '1' WHERE memb___id = ?")
->execute([$account]);
auditar($site, 'ban_account', $account, [
'antes' => $antes['bloc_code'] ?? null,
'depois' => '1',
'motivo' => $motivo,
]);
}
Si el jugador está online, fuerza la desconexión por el mecanismo de tu versión (o avisa que el ban solo surte efecto en el próximo login), para que no siga jugando baneado.
Módulo de envío de ítems y zen
Este es el módulo más peligroso del panel — es donde la economía puede quebrarse. Dos trampas: la duplicación (acreditar dos veces) y el conflicto con el jugador online (el cambio es sobrescrito cuando el juego guarda el personaje). Buenas prácticas:
- Prefiere aplicar con el jugador offline, o usa la cola oficial de "gift"/warehouse de tu distribución, si existe.
- Haz la operación idempotente, con un identificador único por envío.
- Siempre audita el valor, el ítem y el objetivo.
<?php
function darZen(string $account, int $valor, PDO $game, PDO $site): void {
exigirPapel(['admin', 'gm']);
if ($valor <= 0 || $valor > 2000000000) throw new RuntimeException('valor inválido');
// ejemplo: acreditar zen en el warehouse de la cuenta (la columna VARÍA por versión)
$game->prepare("UPDATE warehouse SET Money = Money + ? WHERE AccountID = ?")
->execute([$valor, $account]);
auditar($site, 'give_zen', $account, ['valor' => $valor]);
}
Nunca construyas la query con el valor concatenado, y valida los límites máximos — un GM (o un intruso con su sesión) no debería poder dar miles de millones sin un tope.
Registrando la auditoría
La función de auditoría es llamada por todos los módulos. Es tu caja negra.
<?php
function auditar(PDO $site, string $action, ?string $target, array $details): void {
$stmt = $site->prepare(
"INSERT INTO admin_audit (admin_id, action, target, details, ip)
VALUES (?, ?, ?, ?, ?)"
);
$stmt->execute([
$_SESSION['admin_id'] ?? 0,
$action,
$target,
json_encode($details, JSON_UNESCAPED_UNICODE),
$_SERVER['REMOTE_ADDR'],
]);
}
Trata el log como inmutable: ninguna ruta del panel debe permitir editar ni borrar registros de auditoría. Si un GM abusa, es aquí donde lo pruebas. Considera replicar los logs a otro destino (un archivo o base separada) por si el panel llega a ser comprometido.
Front-end y usabilidad
Mantén la interfaz simple y orientada a la búsqueda: un campo para localizar cuenta/personaje y acciones claras con doble confirmación en las operaciones destructivas.
// doble confirmación antes de banear
function confirmarBan(account) {
if (confirm(`¿Banear la cuenta "${account}"? Esta acción será registrada.`)) {
// envía POST con token CSRF al backend
}
}
Incluye siempre un token CSRF en cada formulario que cambia el estado, para impedir que un sitio malicioso obligue a un admin logueado a ejecutar acciones. Escapa toda salida en HTML para evitar XSS (por ejemplo, al mostrar nombres de personaje que vinieron de la base). Si este panel forma parte de un servidor que estás montando ahora, mira también la guía general de cómo crear un servidor de MU Online para ubicar el panel en la infraestructura.
Errores comunes y soluciones
| Síntoma | Causa probable | Solución |
|---|---|---|
| Panel invadido / ítems duplicados | Sesión de admin secuestrada, sin 2FA | Activa 2FA, restringe IP, regenera la sesión en el login |
| SQL Injection en el módulo de búsqueda | Query concatenada con la entrada | Usa exclusivamente PDO parametrizado |
| El ítem desaparece tras dárselo a un jugador online | El personaje guardado sobrescribió el cambio | Aplícalo con el jugador offline o vía cola oficial |
| No sé quién baneó a un jugador | Falta de auditoría | Registra toda acción en admin_audit |
| Un GM accedió al área de admin | RBAC no verificado en la ruta | Middleware exigirPapel en toda ruta sensible |
| Fuerza bruta en el login | Sin rate limit | Bloquea por intentos/IP y agrega 2FA |
| XSS al mostrar el nombre de un char | Salida sin escapar | Escapa el HTML de todo dato que venga de la base |
Seguridad en profundidad — resumen práctico
- HTTPS obligatorio; panel en subdominio propio, con restricción de IP cuando sea posible.
- Contraseñas en bcrypt/Argon2; 2FA para todos los administradores.
- RBAC verificado en el servidor, nunca solo en el front-end (esconder un botón no es seguridad).
- Todo SQL parametrizado; toda salida escapada; token CSRF en los formularios.
- Base del juego aislada de internet — solo el panel habla con ella.
- Auditoría inmutable de todas las acciones sensibles, con replicación externa.
- Límites de valor en las operaciones económicas (zen, ítems) para contener el abuso.
Lista de verificación de lanzamiento
- Panel servido únicamente vía HTTPS, en subdominio protegido
- Tablas
admin_usersyadmin_auditcreadas - Contraseñas en hash fuerte y 2FA activo para los admins
- Rate limiting en el login implementado
- Middleware de RBAC aplicado en todas las rutas sensibles
- Todas las queries parametrizadas (revisión manual hecha)
- Token CSRF en todos los formularios de escritura
- Nombres reales de tabla/columna del game DB confirmados para tu versión
- Módulo de ítems/zen con límite e idempotencia probado
- Auditoría grabando quién/qué/cuándo/dónde en cada acción
- Base del juego inaccesible desde internet (firewall verificado)
- Prueba de intrusión básica hecha (injection, XSS, escalada de rol)
Con un panel bien construido, dejas de tocar la base a mano, delegas tareas a tu equipo con seguridad, respondes a incidentes con logs reales y proteges el activo más valioso del servidor: la confianza de los jugadores. Constrúyelo capa por capa, prueba cada barrera y trata el panel siempre como si ya estuviera bajo ataque — porque, tarde o temprano, lo estará.
Preguntas frecuentes
¿Necesito un panel web o el Navicat resuelve?
Editar directo en la base funciona al principio, pero es arriesgado y no deja rastro. Un panel web da control de permisos, validaciones, logs de auditoría y permite delegar tareas a GMs sin entregar acceso total a la base.
¿Qué lenguaje usar para el panel de administración?
PHP con PDO es el más común en el mundo MU por correr en el mismo entorno del sitio, pero puedes usar Node.js, Python o .NET. Lo importante es conectarse con seguridad al SQL Server del juego y nunca exponer la base a internet.
¿Cómo proteger el panel del acceso no autorizado?
Usa autenticación fuerte con contraseña en hash, 2FA para administradores, restricción por IP, HTTPS obligatorio, control de permisos por rol y registro de auditoría de todas las acciones sensibles.
¿Es seguro dar ítems y zen desde el panel mientras el jugador está online?
Es arriesgado. Los cambios en el personaje mientras está logueado pueden ser sobrescritos cuando el juego guarda, o causar duplicación. Lo ideal es aplicar los cambios con el jugador offline o usar la cola oficial de warehouse/gift de tu versión.
¿Cómo saber quién hizo cada cambio en el panel?
Con una tabla de auditoría que registra usuario admin, acción, objetivo, datos antes/después, IP y hora. Sin log de auditoría no puedes investigar el abuso de GM ni revertir errores.