Puedes tener un diseño sólido (A04) y un código sin inyecciones (A03), y aun así quedar expuesto por cómo está configurado y desplegado el sistema. A05:2021 – Configuración de Seguridad Incorrecta (Security Misconfiguration) recoge los fallos que no están en tu lógica, sino en los ajustes: valores por defecto inseguros, cuentas y contraseñas de fábrica, mensajes de error que revelan de más, cabeceras de seguridad ausentes, servicios y puertos innecesarios, permisos laxos. Es una de las categorías más frecuentes, precisamente porque el software moderno tiene cientos de opciones y muchas vienen "abiertas por comodidad".
En el Top Ten 2021, esta categoría absorbió el antiguo XXE (XML External Entities), que no es más que un parser XML mal configurado. Aquí lo mencionamos como parte de A05, pero por su técnica específica lo desarrollamos a fondo en la lección 03-07. En BazarNube revisaremos la configuración de la API Express y de los contenedores Docker, dos focos habituales de mala configuración.
Aviso legal y ético: los ejemplos son ilustrativos y con datos ficticios. Practica solo sobre sistemas propios o con autorización explícita.
Contenido
- Qué abarca la configuración incorrecta
- Cabeceras de seguridad ausentes en Express
- Mensajes de error verbosos y modo de desarrollo en producción
- Cuentas y valores por defecto; superficie innecesaria
- Hardening de contenedores Docker
- XXE como configuración de parser (remitido a 03-07)
- Proceso: hardening reproducible y automatizado
- Errores comunes, ejercicios y soluciones
- Qué abarca la configuración incorrecta
Un vistazo a la variedad de fallos que caen bajo A05:
| Área | Fallo típico | Riesgo |
|---|---|---|
| Cabeceras HTTP | Faltan HSTS, X-Content-Type-Options, CSP |
XSS, sniffing, clickjacking |
| Errores | Stack traces al cliente | Fuga de información interna |
| Cuentas | Usuario/clave por defecto sin cambiar | Acceso trivial |
| Servicios | Puertos/paneles de administración expuestos | Superficie de ataque |
| Framework | Modo debug activo en producción |
Fuga de datos, ejecución |
| Parsers | XML con entidades externas habilitadas | XXE (03-07) |
| Permisos | Ficheros/buckets con acceso público | Exposición de datos |
El hilo común: valores por defecto pensados para "que funcione", no para "que sea seguro", que nunca se endurecieron.
- Cabeceras de seguridad ausentes en Express
La API de BazarNube arrancó sin cabeceras de seguridad y, peor, revelando su tecnología:
// app.js — VULNERABLE: sin cabeceras de seguridad, revela X-Powered-By
const express = require('express');
const app = express();
// Express manda por defecto "X-Powered-By: Express" (pista para el atacante)
app.get('/', (req, res) => res.send('BazarNube API'));Cada respuesta anuncia X-Powered-By: Express (facilita al atacante buscar CVEs de esa pila) y no incluye ninguna cabecera defensiva. Corrección con helmet, que fija un conjunto de cabeceras sensatas:
// app.js — SEGURO: helmet aplica cabeceras de seguridad y oculta la tecnologia
const helmet = require('helmet');
app.disable('x-powered-by'); // deja de revelar el framework
app.use(helmet()); // HSTS, X-Content-Type-Options, etc.
app.use(helmet.contentSecurityPolicy({ directives: {
defaultSrc: ["'self'"], scriptSrc: ["'self'"], frameAncestors: ["'none'"]
}}));| Cabecera | Qué hace | Frente a |
|---|---|---|
Strict-Transport-Security |
Fuerza HTTPS | Degradación TLS (ver A02) |
X-Content-Type-Options: nosniff |
Impide adivinar el tipo MIME | Ataques por sniffing |
Content-Security-Policy |
Restringe orígenes de scripts/recursos | XSS (ver 03-04) |
X-Frame-Options/frame-ancestors |
Impide enmarcado | Clickjacking |
Referrer-Policy |
Limita el Referer enviado |
Fuga de URLs |
- Mensajes de error verbosos y modo desarrollo
En producción, un error debe registrar el detalle en el servidor y devolver al cliente un mensaje genérico. BazarNube tenía lo contrario:
// VULNERABLE: envia el stack trace al cliente
app.use((err, req, res, next) => {
res.status(500).send(err.stack); // revela rutas, versiones, estructura
});Un stack trace expone rutas de ficheros, versiones de librerías, nombres de tablas y a veces fragmentos de consultas: un mapa para el atacante. Corrección:
// SEGURO: log interno detallado, respuesta generica
app.use((err, req, res, next) => {
logger.error({ err, path: req.path, reqId: req.id }); // detalle SOLO en el log
res.status(500).json({ error: 'Error interno', reqId: req.id });
});El reqId permite al soporte correlacionar la incidencia con el log sin revelar nada al cliente. Relacionado: desactiva el modo debug/development de frameworks en producción (NODE_ENV=production), que a menudo activa páginas de error detalladas y desactiva optimizaciones de seguridad.
- Cuentas y valores por defecto; superficie innecesaria
- Cuentas por defecto: paneles de administración, bases de datos y servicios suelen traer
admin/admino similares. Cámbialas siempre; deshabilita las que no uses. - Servicios y puertos innecesarios: cada servicio expuesto es superficie de ataque. Si BazarNube no necesita exponer el puerto de PostgreSQL al exterior, no lo publiques.
- Endpoints de diagnóstico: paneles de métricas,
/debug, documentación de API interna... deben estar protegidos o deshabilitados en producción. - Directorios y listados: desactiva el directory listing y no sirvas ficheros de configuración (
.env,.git).
- Hardening de contenedores Docker
BazarNube corre en Docker. Un Dockerfile descuidado es configuración incorrecta de manual:
# Dockerfile — VULNERABLE
FROM node:latest # tag flotante: version impredecible
WORKDIR /app
COPY . . # copia TODO, incluido .env y .git
RUN npm install
USER root # corre como root (por defecto)
CMD ["node", "app.js"]Problemas: imagen base con tag flotante (latest, imposible de reproducir y a menudo con paquetes de más), copia de secretos y del historial Git a la imagen, y ejecución como root (si alguien escapa del proceso, es root en el contenedor). Versión endurecida:
# Dockerfile — SEGURO
FROM node:20.17-slim # version fijada y variante slim (menos superficie)
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev # instalacion reproducible, sin devDependencies
COPY src ./src # copiar solo lo necesario (ver .dockerignore)
RUN addgroup --system app && adduser --system --ingroup app app
USER app # ejecutar como usuario sin privilegios
CMD ["node", "src/app.js"]Y un .dockerignore para no filtrar secretos ni el historial:
| Práctica | Por qué |
|---|---|
| Tag de versión fijado | Reproducibilidad; evita cambios silenciosos |
Variante slim/mínima |
Menos paquetes = menos CVEs (enlaza con A06, 03-08) |
USER no root |
Limita el impacto de un escape del proceso |
.dockerignore |
Evita copiar .env, .git, secretos |
| Escanear la imagen | Detecta vulnerabilidades en capas (SCA de imágenes) |
- XXE como configuración de parser
El antiguo A04:2017 (XXE) quedó absorbido en A05 porque, en el fondo, es un parser XML mal configurado: viene por defecto con el procesamiento de entidades externas activado, y nadie lo desactivó. Al ser un tema con técnica y explotación propias (lectura de ficheros, SSRF, DoS por "billion laughs"), lo desarrollamos completo en la lección 03-07 – Entidades Externas XML (XXE). Recuerda la relación: XXE es un caso concreto de configuración de seguridad incorrecta.
- Proceso: hardening reproducible
La configuración segura no es un ajuste puntual, es un proceso repetible:
flowchart LR A[Baseline de hardening] --> B[Config como codigo] B --> C[Escaneo automatico en CI] C --> D[Mismo entorno dev/staging/prod] D --> A
- Baseline de hardening: una lista de referencia (p. ej. CIS Benchmarks) de cómo debe quedar cada componente.
- Configuración como código: define la config en ficheros versionados (Dockerfile, IaC), no a mano en cada servidor. Así es auditable y reproducible.
- Paridad de entornos: dev, staging y producción deben configurarse igual; muchas brechas nacen de un staging "abierto".
- Escaneo automático: herramientas que revisan cabeceras, config de contenedores e IaC en el pipeline (lo veremos con ZAP en M6 para la parte web).
Errores Comunes y Consejos
- Desplegar con la config por defecto. Casi siempre prioriza "que arranque" sobre "que sea seguro".
- Revelar la tecnología (
X-Powered-By, cabeceras de versión, stack traces). Ocúltalo. - Dejar
debug/developmenten producción. FijaNODE_ENV=productiony equivalentes. - Contenedores como root y con tag
latest. Usa usuario sin privilegios y versiones fijadas. - Copiar
.env/.gita la imagen. Usa.dockerignore. - Configurar a mano cada servidor. Deriva en inconsistencias; usa configuración como código.
- Consejo: trata la configuración con el mismo rigor que el código: versionada, revisada y escaneada en CI.
Ejercicios
Ejercicio 1. Enumera tres problemas de seguridad en este fragmento de despliegue y corrígelos:
const app = express();
app.use(express.json());
app.use((err, req, res, next) => res.status(500).send(err.stack));
// sin helmet, NODE_ENV sin definirEjercicio 2. Un compañero dice: "ocultar X-Powered-By no sirve, el atacante encontrará la tecnología igual". ¿Qué le responderías?
Ejercicio 3. Explica por qué ejecutar el contenedor como root agrava el impacto de otra vulnerabilidad (por ejemplo, una ejecución de comandos como la de A03).
Soluciones
Solución 1. Problemas: (1) no hay cabeceras de seguridad → añadir helmet; (2) el handler de error devuelve el stack al cliente → registrar internamente y responder genérico; (3) NODE_ENV sin definir → fijar production. Además, ocultar X-Powered-By. Ver secciones 2 y 3.
Solución 2. Es cierto que un atacante determinado puede inferir la tecnología por otras vías, pero ocultarla eleva el coste y frena el escaneo automatizado que busca versiones concretas para lanzar exploits conocidos. Es defensa en profundidad: no es la barrera principal, pero suma y no cuesta nada.
Solución 3. Si el proceso corre como root dentro del contenedor y una vulnerabilidad permite ejecutar comandos, esos comandos se ejecutan con privilegios máximos: el atacante puede modificar cualquier fichero del contenedor, instalar herramientas y aumentar la probabilidad de escapar al host. Con un usuario sin privilegios, el radio de daño se reduce notablemente (menor privilegio, defensa en profundidad).
Conclusión
A05 nos recuerda que la seguridad no termina en el código: los valores por defecto, las cabeceras, los errores, las cuentas y los contenedores deben endurecerse de forma explícita, repetible y auditable. En BazarNube añadimos helmet y ocultamos la tecnología, silenciamos los stack traces al cliente, y endurecimos el Dockerfile (versión fijada, usuario no root, .dockerignore). La clave: configuración como código, igual de revisada que la lógica.
Entrada de backlog — A05: helmet + CSP activados y X-Powered-By desactivado; handler de errores genérico con reqId; NODE_ENV=production; Dockerfile endurecido (tag fijado, USER app, .dockerignore); pendiente escaneo de config en CI.
Mencionamos que el XXE es un parser XML mal configurado. El módulo legacy Java de BazarNube procesa XML de facturación, así que es el momento de abrir esa caja. La siguiente lección es Entidades Externas XML (XXE).
Curso de OWASP: Directrices y Estándares para la Seguridad en Aplicaciones Web
Módulo 1: Introducción a OWASP
Módulo 2: Principales Proyectos de OWASP
- OWASP Top Ten
- OWASP ASVS (Application Security Verification Standard)
- OWASP SAMM (Software Assurance Maturity Model)
- OWASP ZAP (Zed Attack Proxy)
- Otros Proyectos Clave: WSTG, Cheat Sheets y Dependency-Check
Módulo 3: OWASP Top Ten 2021 en Profundidad
- A01:2021 – Pérdida de Control de Acceso
- A02:2021 – Fallos Criptográficos y Exposición de Datos Sensibles
- A03:2021 – Inyección
- Cross-Site Scripting (XSS) en Profundidad
- A04:2021 – Diseño Inseguro
- A05:2021 – Configuración de Seguridad Incorrecta
- Entidades Externas XML (XXE)
- A06:2021 – Componentes Vulnerables y Desactualizados
- A07:2021 – Fallos de Identificación y Autenticación
- A08:2021 – Fallos de Integridad de Software y Datos (Deserialización Insegura)
- A09:2021 – Fallos de Registro y Monitorización
- A10:2021 – Server-Side Request Forgery (SSRF)
Módulo 4: OWASP ASVS (Application Security Verification Standard)
- Introducción a ASVS
- Niveles de Verificación
- Requisitos de Seguridad
- Implementación de ASVS en Proyectos
Módulo 5: OWASP SAMM (Software Assurance Maturity Model)
Módulo 6: OWASP ZAP (Zed Attack Proxy)
- Introducción a ZAP
- Instalación y Configuración
- Escaneo de Vulnerabilidades
- Automatización de Pruebas de Seguridad
Módulo 7: Buenas Prácticas y Recomendaciones
- Ciclo de Vida de Desarrollo Seguro (SDLC)
- Modelado de Amenazas (Threat Modeling)
- Integración de Seguridad en DevOps (DevSecOps)
- Capacitación y Concienciación en Seguridad
- Herramientas y Recursos Adicionales
Módulo 8: Ejercicios Prácticos y Casos de Estudio
- Ejercicio 1: Identificación de Vulnerabilidades
- Ejercicio 2: Implementación de Controles de Seguridad
- Caso de Estudio 1: Análisis de un Incidente de Seguridad
- Caso de Estudio 2: Mejora de la Seguridad en una Aplicación Web
