CreaRack-SL

T4 · Handler global de excepciones sin fuga de información (Auditoría Suprema Etapa 3)

Contexto

La API de CreaRack (~509 endpoints Ninja) devolvía en respuestas 500 el mensaje técnico completo del error junto con el nombre de la clase de excepción:

{
  "error": "Unhandled API error...<ruta-interna-secreta>/opt/app/...",
  "type": "SomeException"
}

Esto exponía:

  • Rutas internas del servidor
  • Fragmentos de consultas SQL
  • Nombres de máquinas / configuración de infraestructura
  • Detalles de la pila de tecnología

Hallazgo transversal de blueprints s105 (auditoría de seguridad): afectaba al 100% de los endpoints por ser la política global de manejo de excepciones en config/urls.py.

Decisión

Implementar un handler global de excepciones (global_exception_handler) que:

  1. En producción (DEBUG=False): Devuelve un cuerpo genérico y fijo:

    {
      "error": "Internal server error"
    }
  2. Logs del servidor: El traceback completo se registra en api.errors con contexto completo (método HTTP, path, stack).

  3. En desarrollo local (DEBUG=True): Se conservan detail (str(exc)) y type (clase) para facilitar debugging.

  4. Contrato API: El formato {"error": ...} se mantiene intacto → Sin cambios en el frontend.

Implementación

Archivo: config/urls.py / función global_exception_handler

import logging

_api_logger = logging.getLogger("api.errors")

@api.exception_handler(Exception)
def global_exception_handler(request, exc):
    """500 genérico (T4, Auditoría Suprema): el detalle del error iba al cliente..."""
    _api_logger.error("Unhandled API error on %s %s", request.method, request.path, exc_info=exc)
    body = {"error": "Internal server error"}
    if settings.DEBUG:
        body["detail"] = str(exc)
        body["type"] = exc.__class__.__name__
    return api.create_response(request, body, status=500)

Tests: tests/api/test_t4_exception_handler.py

  • test_500_body_is_generic: Verifica que con DEBUG=False, el cuerpo no expone información sensible.
  • test_debug_keeps_detail_for_local_dev: Verifica que con DEBUG=True, el detail sigue presente.

Justificación

  • Seguridad: Cierra una fuga de información transversal que afectaba a todos los 509 endpoints.
  • Auditoría: Requisito s105 de la auditoría de seguridad pre-product-launch.
  • Observabilidad: La información se conserva en los logs del servidor (api.errors), accesibles solo para ops/dev.
  • Compatibilidad: El frontend no necesita cambios (nadie consumía el campo type del 500).

Relacionadas

  • [[decision—20260611—t1-cifrado-credenciales-snmp]] — Otra mitigación de T1 en la misma auditoría
  • [[concept—security—information-disclosure]] — Concepto de fuga de información

Véase también

  • [[entity—config—handler—global-exception]]
  • [[concept—security—information-disclosure]]
  • [[runbook—security—respond-error-disclosure]]