Otros

Son tantos los software usados...

Solución a problemas de carga de imágenes externas en MantisBT

Problema

Al intentar mostrar imágenes alojadas en dominios externos dentro de los tickets de MantisBT, las imágenes no se cargan correctamente a pesar de que la sintaxis Markdown utilizada es correcta:

![Nombre de la imagen](https://dominio-externo.com/ruta/imagen.jpg)

El navegador bloquea la carga de estas imágenes aunque la URL sea accesible directamente.

Análisis mediante herramientas de desarrollo web

Al inspeccionar la consola del navegador, aparecen errores similares a:

Refused to load the image 'https://dominio-externo.com/imagenes/sitelight/App_500.jpg' because it violates the following Content Security Policy directive: "img-src 'self' data:".

Este error indica que MantisBT está implementando una política de seguridad de contenido (CSP) que restringe la carga de imágenes únicamente desde:

Documentación oficial relevante

La configuración de seguridad de MantisBT se explica en la documentación oficial:

Según la documentación, MantisBT implementa varias cabeceras de seguridad para proteger la aplicación, incluyendo Content-Security-Policy que puede ser personalizada mediante la configuración $g_custom_headers.

Soluciones

Solución 1: Desactivación total de la política CSP (NO RECOMENDADA)

Esta solución elimina completamente la protección CSP, lo que supone un riesgo de seguridad y NO se recomienda excepto para entornos de prueba aislados.

// Añadir al archivo config_inc.php
$g_custom_headers = array( 'Content-Security-Policy:' );

⚠️⚠️⚠️ ADVERTENCIA: Esta configuración elimina toda la protección CSP, exponiendo potencialmente la aplicación a ataques XSS y de inyección de contenido. No usar en entornos de producción.

Solución 2: Permitir dominios específicos (RECOMENDADA)

Esta solución mantiene la seguridad general mientras permite imágenes de dominios concretos:

// Añadir al archivo config_inc.php
$g_custom_headers = array(
    'Content-Security-Policy: default-src \'self\'; img-src \'self\' data: https://dominio-autorizado.com; script-src \'self\' \'unsafe-inline\' \'unsafe-eval\'; style-src \'self\' \'unsafe-inline\'; frame-ancestors \'self\';'
);

Para permitir múltiples dominios:

// Añadir al archivo config_inc.php
$g_custom_headers = array(
    'Content-Security-Policy: default-src \'self\'; img-src \'self\' data: https://dominio1.com https://dominio2.com https://*.subdominio.com; script-src \'self\' \'unsafe-inline\' \'unsafe-eval\'; style-src \'self\' \'unsafe-inline\'; frame-ancestors \'self\';'
);

Para permitir cualquier dominio para imágenes (menos seguro):

// Añadir al archivo config_inc.php
$g_custom_headers = array(
    'Content-Security-Policy: default-src \'self\'; img-src * data:; script-src \'self\' \'unsafe-inline\' \'unsafe-eval\'; style-src \'self\' \'unsafe-inline\'; frame-ancestors \'self\';'
);

Explicación de la política CSP

La directiva Content-Security-Policy utilizada en la solución mantiene:

Verificación

Después de aplicar la configuración:

  1. Guarde los cambios en el archivo config_inc.php
  2. Limpie la caché del navegador
  3. Recargue la página de MantisBT
  4. Verifique que las imágenes desde los dominios autorizados se cargan correctamente

Solución de problemas

Si después de aplicar la configuración las imágenes siguen sin cargarse:

  1. Verifique que la URL de la imagen es accesible directamente
  2. Compruebe que la sintaxis de la directiva CSP es correcta (comillas simples, espacios entre dominios)
  3. Examine la consola del navegador para ver mensajes de error específicos
  4. Asegúrese de que no haya otras restricciones a nivel de servidor web (Apache, Nginx) que puedan estar afectando
Aviso

Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad. El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de Castris

Si necesitas soporte profesional puedes contratar con Castris soporte profesional.

Kimai : Time recording could not be started [undefined]

Contexto. Compatibilidad entre Kimai y ModSecurity (OWASP CRS): Diagnóstico y solución de falsos positivos

En una instalación de Kimai (versión 2.36.1) empece a sufrir un problema que no me permitía, continuar, parar ningún proceso de mi tablero de control de tiempos.

Sin embargo, si podía editarlos, o crearlos desde cero.

Me pillo, en mal momento y no se me ocurrio, mirar las Webmaster Tools del navegador para ver algo, y me limite a revisar los logs de la app.

Tras un desafortunado encuentro con el desarollador en su github, en el que de poco, me acuso de mentiroso, me puse las pilas y dedicí dedicarle un tiempo.

Análisis profundo

Kimai - Issue undifined

Los síntomas observados fueron:

Dada la ausencia de logs, y por las características de la app, pense en un problema de javascript, ya fuera pro acción o por implicación.

Abrí las web master tools, y allí estaba.

Webmaster tools - error 404

Mi servidor, era el que interceptaba y anulaba las peticiones antes de que la aplicación pudiera gestionarlas.

¿Cómo podía haber sido tan descuidado?

Mi entorno de sistemas esta altamente protegido pro ModSecurity + OSWAP Rules. Y estaba teniendo un hermoso 404, que la app era incapaz de apoyar en el mensaje de error.

Al analizar los logs de mod_security, se identificaron errores silenciosos al interactuar con determinadas funciones de la aplicación, especialmente en rutas protegidas como:

Diagnóstico técnico

Cómo identificar los falsos positivos

Directadmin (nginx)

En mi caso es usar el gestor de Mod Security de Directadmin

Apache

# Buscar bloqueos relacionados con Kimai
tail -f /var/log/apache2/modsec_audit.log | grep -E "(timesheet|kimai)"

# Extraer IDs de reglas que están bloqueando
grep -o "id:[0-9]*" /var/log/apache2/modsec_audit.log | sort | uniq -c

Las reglas que generaron el bloqueo fueron:

Regla 911100 – HTTP Method not allowed

Ejemplo de traza:

[2025-01-30 10:15:23.123456] [client 192.168.1.100] ModSecurity: Access denied with code 403 (phase 2). 
Matched "Operator `Within' with parameter `GET HEAD POST OPTIONS'" 
against variable `REQUEST_METHOD' (Value: `PATCH')
[file "/etc/modsecurity/crs/rules/REQUEST-911-METHOD-ENFORCEMENT.conf"] 
[line "27"] [id "911100"]

Regla 930120 – LFI attempt (false positive)

Ejemplo:

Matched "Operator `PmFromFile' with parameter `lfi-os-files.data'" 
against variable `ARGS:timesheet_edit_form[description]'

Solución propuesta

Dado que las rutas afectadas están protegidas por autenticación y son de uso legítimo dentro del flujo de Kimai, se optó por desactivar selectivamente las reglas conflictivas únicamente para esas rutas, utilizando exclusiones personalizadas de ModSecurity.

Puedes ver mas información en Mod Security. Desactivación global de reglas por path

Reglas de exclusión recomendadas (ModSecurity v3 / OWASP)

Archivo: REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf

SecRule REQUEST_URI "@rx ^/(en/timesheet/\d+/edit|api/timesheets/\d+/(restart|stop))$" \
    "id:11010,\
    phase:1,\
    pass,\
    nolog,\
    ctl:ruleRemoveById=911100,\
    ctl:ruleRemoveById=930120"

Opcional: si se desea una exclusión más general para toda la API de timesheets:

SecRule REQUEST_URI "@beginsWith /api/timesheets/" \
    "id:11011,\
    phase:1,\
    pass,\
    nolog,\
    ctl:ruleRemoveById=911100"

Verificación de la solución

Comprobar que las reglas se han cargado correctamente:

# Verificar la sintaxis de ModSecurity
nginx -t

# Recargar la configuración
systemctl reload nginx

Testear las funcionalidades afectadas:

  1. Editar un timesheet existente
  2. Reiniciar/detener un timer via API
  3. Verificar que no aparecen más bloqueos en modsec_audit.log

Consideraciones de seguridad

⚠️ NOTA IMPORTANTE: Sobre la desactivación global de reglas

Una práctica lamentablemente extendida en entornos de hosting profesional es la desactivación global de reglas ModSecurity ante el primer falso positivo. Esto representa un grave error de seguridad que debe evitarse a toda costa.

Por qué es una mala práctica:

La forma correcta:

En entornos de hosting compartido, donde conviven múltiples aplicaciones y clientes, esta disciplina no es opcional: es una responsabilidad profesional fundamental.

Alternativas más granulares

Si se requiere mayor seguridad, se pueden implementar exclusiones condicionales:

# Solo para usuarios autenticados (verificando cookie de sesión)
SecRule REQUEST_URI "@rx ^/api/timesheets/\d+/(restart|stop)$" \
    "id:11012,\
    phase:1,\
    pass,\
    nolog,\
    chain"
    SecRule REQUEST_COOKIES:PHPSESSID "@rx ^[a-zA-Z0-9]{26,32}$" \
        "ctl:ruleRemoveById=911100"

# Solo desde IPs internas
SecRule REQUEST_URI "@rx ^/api/timesheets/" \
    "id:11013,\
    phase:1,\
    pass,\
    nolog,\
    chain"
    SecRule REMOTE_ADDR "@ipMatch 192.168.0.0/16,10.0.0.0/8" \
        "ctl:ruleRemoveById=911100"

Conclusión

Kimai, como muchas aplicaciones modernas que emplean métodos HTTP extendidos (PUT, PATCH, DELETE), puede encontrarse con bloqueos inesperados si se implementa un WAF sin ajustar sus reglas a los flujos funcionales reales.

Este caso demuestra la importancia de realizar una auditoría cruzada entre el comportamiento esperado de la aplicación y las políticas de seguridad impuestas por el entorno.

Documentar y aislar los falsos positivos ayuda no solo a mantener el entorno seguro, sino también a garantizar la funcionalidad legítima para el usuario final.

Referencias

Aviso

Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad. El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de Castris

Si necesitas soporte profesional puedes contratar con Castris soporte profesional.