Mejores Prácticas de Seguridad
2.5.x ✅ 4.0.x ✅
Este documento proporciona mejores prácticas completas de seguridad para desarrolladores de módulos XOOPS. Seguir estas guías ayudará a asegurar que sus módulos sean seguros y no introduzcan vulnerabilidades en instalaciones XOOPS.
Principios de Seguridad
Sección titulada «Principios de Seguridad»Todo desarrollador XOOPS debe seguir estos principios fundamentales de seguridad:
- Defensa en Profundidad: Implementar múltiples capas de controles de seguridad
- Menor Privilegio: Proporcionar solo los derechos de acceso mínimos necesarios
- Validación de Entrada: Nunca confiar en entrada del usuario
- Seguro por Defecto: La seguridad debe ser la configuración predeterminada
- Mantenerlo Simple: Los sistemas complejos son más difíciles de asegurar
Documentación Relacionada
Sección titulada «Documentación Relacionada»- CSRF-Protection - Sistema de tokens y clase XoopsSecurity
- Input-Sanitization - MyTextSanitizer y validación
- SQL-Injection-Prevention - Prácticas de seguridad de base de datos
Referencia Rápida de Lista de Verificación
Sección titulada «Referencia Rápida de Lista de Verificación»Antes de lanzar su módulo, verifique:
- Todos los formularios incluyen tokens XOOPS
- Toda entrada del usuario es validada y sanitizada
- Toda salida está escapada apropiadamente
- Todas las consultas de base de datos usan sentencias parametrizadas
- Las cargas de archivos están validadas apropiadamente
- Las comprobaciones de autenticación y autorización están en su lugar
- El manejo de errores no revela información sensible
- La configuración sensible está protegida
- Las bibliotecas de terceros están actualizadas
- Se ha realizado prueba de seguridad
Autenticación y Autorización
Sección titulada «Autenticación y Autorización»Comprobación de Autenticación del Usuario
Sección titulada «Comprobación de Autenticación del Usuario»// Verificar si el usuario está conectadoif (!is_object($GLOBALS['xoopsUser'])) { redirect_header(XOOPS_URL, 3, _NOPERM); exit();}Comprobación de Permisos del Usuario
Sección titulada «Comprobación de Permisos del Usuario»// Verificar si el usuario tiene permiso para acceder a este móduloif (!$GLOBALS['xoopsUser']->isAdmin($xoopsModule->mid())) { redirect_header(XOOPS_URL, 3, _NOPERM); exit();}
// Verificar permiso específico$moduleHandler = xoops_getHandler('module');$module = $moduleHandler->getByDirname('mymodule');$moduleperm_handler = xoops_getHandler('groupperm');$groups = $GLOBALS['xoopsUser']->getGroups();
if (!$moduleperm_handler->checkRight('mymodule_view', $item_id, $groups, $module->getVar('mid'))) { redirect_header(XOOPS_URL, 3, _NOPERM); exit();}Configuración de Permisos de Módulo
Sección titulada «Configuración de Permisos de Módulo»// Crear permiso en función de instalación/actualización$gpermHandler = xoops_getHandler('groupperm');$gpermHandler->deleteByModule($module->getVar('mid'), 'mymodule_view');
// Añadir permiso para todos los grupos$groups = [XOOPS_GROUP_ADMIN, XOOPS_GROUP_USERS, XOOPS_GROUP_ANONYMOUS];foreach ($groups as $group_id) { $gpermHandler->addRight('mymodule_view', 1, $group_id, $module->getVar('mid'));}Seguridad de Sesión
Sección titulada «Seguridad de Sesión»Mejores Prácticas de Manejo de Sesión
Sección titulada «Mejores Prácticas de Manejo de Sesión»- No almacenar información sensible en la sesión
- Regenerar IDs de sesión después de iniciar sesión/cambios de privilegio
- Validar datos de sesión antes de usarlos
// Regenerar ID de sesión después de iniciar sesiónsession_regenerate_id(true);
// Validar datos de sesiónif (isset($_SESSION['mymodule_user_id'])) { $user_id = (int)$_SESSION['mymodule_user_id']; // Verificar que el usuario existe en la base de datos}Prevención de Fijación de Sesión
Sección titulada «Prevención de Fijación de Sesión»// Después de un inicio de sesión exitososession_regenerate_id(true);$_SESSION['mymodule_user_ip'] = $_SERVER['REMOTE_ADDR'];
// En solicitudes posterioresif ($_SESSION['mymodule_user_ip'] !== $_SERVER['REMOTE_ADDR']) { // Posible intento de secuestro de sesión session_destroy(); redirect_header('index.php', 3, 'Session error'); exit();}Seguridad de Carga de Archivos
Sección titulada «Seguridad de Carga de Archivos»Validación de Cargas de Archivos
Sección titulada «Validación de Cargas de Archivos»// Verificar si el archivo fue cargado apropiadamenteif (!isset($_FILES['userfile']) || $_FILES['userfile']['error'] != UPLOAD_ERR_OK) { redirect_header('index.php', 3, 'File upload error'); exit();}
// Verificar tamaño de archivoif ($_FILES['userfile']['size'] > 1000000) { // Límite 1MB redirect_header('index.php', 3, 'File too large'); exit();}
// Verificar tipo de archivo$allowed_types = ['image/jpeg', 'image/png', 'image/gif'];if (!in_array($_FILES['userfile']['type'], $allowed_types)) { redirect_header('index.php', 3, 'Invalid file type'); exit();}
// Validar extensión de archivo$filename = $_FILES['userfile']['name'];$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));$allowed_extensions = ['jpg', 'jpeg', 'png', 'gif'];if (!in_array($ext, $allowed_extensions)) { redirect_header('index.php', 3, 'Invalid file extension'); exit();}Usar Cargador XOOPS
Sección titulada «Usar Cargador XOOPS»include_once XOOPS_ROOT_PATH . '/class/uploader.php';
$allowed_mimetypes = ['image/gif', 'image/jpeg', 'image/png'];$maxsize = 1000000; // 1MB$maxwidth = 1024;$maxheight = 768;$upload_dir = XOOPS_ROOT_PATH . '/uploads/mymodule';
$uploader = new XoopsMediaUploader( $upload_dir, $allowed_mimetypes, $maxsize, $maxwidth, $maxheight);
if ($uploader->fetchMedia('userfile')) { $uploader->setPrefix('mymodule_');
if ($uploader->upload()) { $filename = $uploader->getSavedFileName(); // Guardar nombre de archivo en base de datos } else { echo $uploader->getErrors(); }} else { echo $uploader->getErrors();}Almacenamiento Seguro de Archivos Cargados
Sección titulada «Almacenamiento Seguro de Archivos Cargados»// Definir directorio de carga fuera de raíz web$upload_dir = XOOPS_VAR_PATH . '/uploads/mymodule';
// Crear directorio si no existeif (!is_dir($upload_dir)) { mkdir($upload_dir, 0755, true);}
// Mover archivo cargadomove_uploaded_file($_FILES['userfile']['tmp_name'], $upload_dir . '/' . $safe_filename);Manejo de Errores y Logging
Sección titulada «Manejo de Errores y Logging»Manejo de Errores Seguro
Sección titulada «Manejo de Errores Seguro»try { $result = someFunction(); if (!$result) { throw new Exception('Operation failed'); }} catch (Exception $e) { // Registrar el error xoops_error($e->getMessage());
// Mostrar un mensaje de error genérico al usuario redirect_header('index.php', 3, 'An error occurred. Please try again later.'); exit();}Registro de Eventos de Seguridad
Sección titulada «Registro de Eventos de Seguridad»// Registrar eventos de seguridadxoops_loadLanguage('logger', 'mymodule');$GLOBALS['xoopsLogger']->addExtra('Security', 'Failed login attempt for user: ' . $username);Seguridad de Configuración
Sección titulada «Seguridad de Configuración»Almacenamiento de Configuración Sensible
Sección titulada «Almacenamiento de Configuración Sensible»// Definir ruta de configuración fuera de raíz web$config_path = XOOPS_VAR_PATH . '/configs/mymodule/config.php';
// Cargar configuraciónif (file_exists($config_path)) { include $config_path;} else { // Manejar configuración faltante}Protección de Archivos de Configuración
Sección titulada «Protección de Archivos de Configuración»Use .htaccess para proteger archivos de configuración:
# En .htaccess<Files "config.php"> Order Allow,Deny Deny from all</Files>Bibliotecas de Terceros
Sección titulada «Bibliotecas de Terceros»Selección de Bibliotecas
Sección titulada «Selección de Bibliotecas»- Elegir bibliotecas mantenidas activamente
- Verificar vulnerabilidades de seguridad
- Verificar que la licencia de la biblioteca es compatible con XOOPS
Actualización de Bibliotecas
Sección titulada «Actualización de Bibliotecas»// Verificar versión de bibliotecaif (version_compare(LIBRARY_VERSION, '1.2.3', '<')) { xoops_error('Please update the library to version 1.2.3 or higher');}Aislamiento de Bibliotecas
Sección titulada «Aislamiento de Bibliotecas»// Cargar biblioteca de forma controladafunction loadLibrary($file){ $allowed = ['parser.php', 'formatter.php'];
if (!in_array($file, $allowed)) { return false; }
include_once XOOPS_ROOT_PATH . '/modules/mymodule/libraries/' . $file; return true;}Pruebas de Seguridad
Sección titulada «Pruebas de Seguridad»Lista de Verificación de Pruebas Manuales
Sección titulada «Lista de Verificación de Pruebas Manuales»- Probar todos los formularios con entrada inválida
- Intentar eludir autenticación y autorización
- Probar funcionalidad de carga de archivos con archivos maliciosos
- Verificar vulnerabilidades XSS en toda salida
- Probar inyección SQL en todas las consultas de base de datos
Pruebas Automatizadas
Sección titulada «Pruebas Automatizadas»Usar herramientas automatizadas para escanear vulnerabilidades:
- Herramientas de análisis de código estático
- Escáneres de aplicaciones web
- Verificadores de dependencia para bibliotecas de terceros
Escape de Salida
Sección titulada «Escape de Salida»Contexto HTML
Sección titulada «Contexto HTML»// Para contenido HTML regularecho htmlspecialchars($variable, ENT_QUOTES, 'UTF-8');
// Usar MyTextSanitizer$myts = MyTextSanitizer::getInstance();echo $myts->htmlSpecialChars($variable);Contexto JavaScript
Sección titulada «Contexto JavaScript»// Para datos usados en JavaScriptecho json_encode($variable);
// Para JavaScript en líneaecho 'var data = ' . json_encode($variable) . ';';Contexto de URL
Sección titulada «Contexto de URL»// Para datos usados en URLsecho htmlspecialchars(urlencode($variable), ENT_QUOTES, 'UTF-8');Variables de Plantilla
Sección titulada «Variables de Plantilla»// Asignar variables a plantilla Smarty$GLOBALS['xoopsTpl']->assign('title', htmlspecialchars($title, ENT_QUOTES, 'UTF-8'));
// Para contenido HTML que debe mostrarse tal cual$GLOBALS['xoopsTpl']->assign('content', $myts->displayTarea($content, 1, 1, 1, 1, 1));Recursos
Sección titulada «Recursos»#security #best-practices #xoops #module-development #authentication #authorization