Gestión de Errores: Cómo no regalar las llaves de tu servidor en un error 404

Imagina la escena: un usuario (o un script automatizado) introduce una comilla simple en el buscador de tu web. En lugar de recibir un educado mensaje de «no se encontraron resultados», la pantalla escupe un bloque de texto incomprensible para un usuario normal, pero que es oro puro para un atacante: un stack trace completo, el usuario de la base de datos y la ruta absoluta donde se aloja el proyecto.

Esto ocurre todos los días. Los frameworks de desarrollo actuales están diseñados para ser increíblemente amigables con el programador, ofreciendo información detallada cuando algo falla. El problema viene cuando ese entorno pasa a producción un viernes por la tarde y a alguien se le olvida cambiar la variable de entorno DEBUG=True.

Gestionar los errores no va solo de que la web no se caiga, va de controlar exactamente qué información revelas al exterior. Un servidor demasiado «hablador» le hace el trabajo de reconocimiento al atacante. Vamos a ver cómo aplicar un enfoque práctico para que tus errores dejen de ser un mapa del tesoro.

El peligro de ser demasiado «hablador» (Information Leakage)

En el ciclo de vida del desarrollo, el detalle es tu amigo. Si una consulta a la base de datos falla, necesitas saber en qué línea exacta del controlador ocurrió y qué parámetros provocaron el fallo.

Sin embargo, en ciberseguridad existe una fase inicial en todo ataque llamada reconocimiento (reconnaissance. Durante esta fase, el atacante no intenta romper tu sistema de inmediato; primero intenta entender cómo está construido. Para ello, forzará errores a propósito: enviará un JSON mal formado, usará métodos HTTP no permitidos (como un PUT donde solo se espera un POST) o inyectará caracteres especiales.

Si tu servidor responde vomitando información de depuración, acabas de ahorrarle horas de escaneo. Has pasado de ser una caja negra a un sistema con sus versiones y estructura de carpetas expuestas. A esto se le conoce como Information Exposure Through an Error Message (clasificado habitualmente bajo el paraguas del CWE-209).

Qué información NUNCA debe ver un usuario final

Por regla general, todo lo que exponga la infraestructura subyacente debe bloquearse antes de llegar al navegador del cliente. Esto incluye:

  • Stack traces (trazas de pila): Muestran la secuencia de llamadas a funciones que llevaron al error.
  • Sintaxis SQL: Nombres de tablas, columnas o el propio motor de base de datos (revelar que usas MariaDB 10.5 es dar pistas para buscar exploits específicos).
  • Rutas internas del servidor: Algo como /var/www/html/wp-content/... o C:\inetpub\wwwroot\... revela el sistema operativo y la estructura de directorios.
  • Versiones de software y librerías: Nginx, Apache, PHP, Node.js o el framework utilizado no deben anunciar su versión exacta en las cabeceras de error
  • Variables de entorno y tokens: En el peor de los casos, un error de conexión puede llegar a imprimir en pantalla la cadena de conexión completa, incluyendo contraseñas.

Anatomía de un desastre: El error tóxico vs. El error seguro

Para entender la diferencia, veamos un ejemplo de un error de base de datos mal gestionado en PHP (usando PDO), muy típico en aplicaciones legacy o mal configuradas:

❌ El error tóxico (Lo que ve el atacante):

Fatal error: Uncaught PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'admin''' at line 1 in /var/www/vhosts/empresa.com/httpdocs/login.php:14
Stack trace:
#0 /var/www/vhosts/empresa.com/httpdocs/login.php(14): PDO->query('SELECT * FROM u...')
#1 {main} thrown in /var/www/vhosts/empresa.com/httpdocs/login.php on line 14
Plaintext

¿Qué acaba de aprender el atacante?

  1. Que es vulnerable a inyección SQL (el error 1064 de sintaxis lo confirma).
  2. Que la base de datos es MySQL.
  3. La ruta absoluta del servidor (/var/www/vhosts/empresa.com/httpdocs/).
  4. El nombre del archivo vulnerable (login.php).

✅ El error seguro (Lo que debe ver el cliente):

HTTP 500 Internal Server Error
Vaya, algo ha salido mal en nuestros servidores.
Por favor, inténtalo de nuevo más tarde o contacta con soporte indicando este código: [ERR-8492-B]
Plaintext

¿Qué acaba de aprender el atacante?

  1. Absolutamente nada útil.
  2. Que quizás sea mejor invertir esfuerzos en otra parte.

El equilibrio: Registro interno detallado vs. Respuesta externa opaca

A veces, al intentar aplicar seguridad, los equipos de IT cometen el error opuesto: silencian los errores por completo (el clásico try { ... } catch { return false; } sin registrar nada). Esto es un infierno para la mantenibilidad. Si un cliente legítimo no puede comprar y no tienes logs, estás ciego.

El criterio editorial aquí es claro: ocultar la información al usuario no significa ignorarla.

El flujo profesional es implementar un ID de correlación. Cuando ocurre una excepción no controlada en tu código:

  1. El sistema genera un ID único y aleatorio (ej. ERR-8492-B).
  2. El sistema guarda en el log del servidor (o en un sistema centralizado como un SIEM) todo el stack trace tóxico, asociado a ese ID.
  3. El servidor devuelve al usuario una vista genérica y amable, mostrando solo ese ID.

De esta forma, si un usuario se queja a soporte, el administrador de sistemas solo tiene que hacer un grep o buscar en su panel de logs ese ID para ver la radiografía exacta del problema, sin haber expuesto nada a internet.

Buenas prácticas para una gestión de errores a prueba de balas

Si quieres auditar tu aplicación hoy mismo, revisa esta checklist de mínimos:

  • Desactiva el modo de depuración en producción: Asegúrate de que variables como WP_DEBUG (WordPress), APP_DEBUG (Laravel) o equivalentes están en false. Es el error número uno.
  • Captura global de excepciones: Configura un handler global en tu aplicación que atrape cualquier error no gestionado para asegurar que siempre se devuelva una respuesta genérica estándar, evitando que el lenguaje subyacente tome el control y escupa el error crudo.
  • Sanitiza la entrada (Input Validation): La mejor manera de gestionar un error es evitar que se produzca. Si esperas un número entero en un ID, rechaza la petición en la primera capa si llega una letra, en lugar de dejar que la base de datos explote intentando procesarlo.
  • Personaliza los errores del servidor web: Nginx y Apache tienen páginas de error por defecto que revelan la versión del servidor (ej. 404 Not Found - nginx/1.18.0). Configura páginas HTML estáticas personalizadas y desactiva la firma del servidor (server_tokens off; en Nginx o ServerSignature Off en Apache).

Conclusión

La seguridad por oscuridad (esconder algo y esperar que no lo encuentren) no es una estrategia de ciberseguridad válida por sí sola. Sin embargo, en el contexto de la gestión de errores, no darle pistas al enemigo es simplemente sentido común.

Cada dato técnico que tu servidor revela en un mensaje de error es una pieza del puzle que le ahorras a un atacante. Configura tus logs para que sean todo lo detallados y ruidosos que tu equipo necesite internamente, pero de cara a internet, tu servidor debe ser una tumba.

Carlos del Río Sáez
Especialista en tecnología y ciberseguridad con más de 17 años de experiencia en entornos educativos y corporativos. Enfocado en sistemas, IA aplicada y ciberdefensa. Ingeniero informático. Miembro de ISC2 (CC Certified) y Security+ Certified.

Suscríbete a Rutaciber.com

El objetivo principal de Rutaciber.com es educar en ciberseguridad. Suscríbete aquí y recibe nuevos artículos en tu email 👇

Sin spam, solo nuevos artículos | Política de privacidad

Artículos relacionados...