El módulo 7 terminó con una promesa: teníamos el proceso completo ensamblado —S-SDLC, threat modeling, DevSecOps, capacitación y un stack de herramientas para BazarNube— y llegaba el momento de aplicarlo con las manos. Este módulo cumple esa promesa. En estas cuatro lecciones dejamos de describir el proceso y lo ejecutamos sobre BazarNube: dos ejercicios guiados de identificación y remediación, y dos casos de estudio de análisis de incidente y de mejora. Empezamos por el principio de cualquier trabajo de AppSec: encontrar lo que está mal. En este laboratorio recibes un fragmento real de la aplicación —varios endpoints Node/Express, un componente React y su configuración— y tu tarea es identificar, clasificar y documentar cada vulnerabilidad como lo haría un analista, combinando revisión de código y escaneo con ZAP (visto a fondo en el módulo 6). No repares nada todavía: eso es el ejercicio 2. Aquí solo cazamos y catalogamos.
Contenido
- Objetivo y entorno del laboratorio
- Metodología: cómo abordar la identificación
- El checklist basado en Top Ten y ASVS
- El código a revisar: endpoints, componente y configuración
- Cómo documentar un hallazgo y registrarlo en el backlog
- Errores comunes y consejos
- Ejercicios
- Conclusión
Objetivo y entorno del laboratorio
Trabajas como AppSec en BazarNube junto a Lucía (backend lead) y Marc (frontend). La SRE ha desplegado en el entorno de staging una nueva rama del servicio de pedidos y catálogo, y antes de aprobar el merge debes revisarla. Dispones de dos entradas de información:
- El código fuente del fragmento (abajo), sobre el que harás una revisión manual.
- Un escaneo ZAP de la instancia de staging ya en marcha, cuyo informe consultarás para confirmar hallazgos y encontrar los que el código no revela a simple vista.
El objetivo del laboratorio no es explotar nada a fondo, sino identificar y clasificar: por cada problema, determinar su categoría del OWASP Top Ten 2021, su severidad, la evidencia que lo respalda y el requisito ASVS que incumple. El resultado es una lista de hallazgos lista para entrar en el backlog BZN-xxx, exactamente el formato que ZAP ya alimentaba en 06-03.
Recordatorio ético: todo esto se hace sobre nuestra aplicación en nuestro staging. La identificación de vulnerabilidades solo es legítima sobre sistemas propios o con autorización explícita, como establecimos en el módulo 1.
Metodología: cómo abordar la identificación
Improvisar lleva a olvidos. Seguimos un método repetible que combina las dos técnicas:
graph LR
A[Entender el contexto] --> B[Revision de codigo guiada por checklist]
B --> C[Escaneo ZAP pasivo y activo]
C --> D[Correlacionar codigo y alertas]
D --> E[Triaje: confirmar o descartar]
E --> F[Documentar cada hallazgo BZN]
- Entender el contexto. ¿Qué hace este código? ¿Qué datos maneja? En BazarNube hay pedidos, precios, datos personales y pagos: cualquier fallo aquí toca dinero o PII.
- Revisión de código guiada por checklist. No leas el código "a ver qué encuentras"; recórrelo con una lista de preguntas concretas (siguiente sección). Presta atención especial a los cruces de límite de confianza del threat model de 07-02: entrada del usuario, consultas a BD, llamadas a terceros.
- Escaneo ZAP. El escaneo pasivo detecta cabeceras y cookies mal configuradas; el activo prueba inyección, XSS o path traversal. No re-explicamos ZAP: lo usamos como en 06-03.
- Correlacionar. Cada alerta de ZAP debe poder señalarse en el código, y cada sospecha del código debe intentar confirmarse dinámicamente. Lo que aparece por ambas vías es casi siempre real.
- Triaje. Descarta falsos positivos con evidencia (reproducir la petición), no por intuición.
- Documentar. Un hallazgo sin ficha reproducible no existe para el equipo.
El checklist basado en Top Ten y ASVS
Este es el guion de preguntas con el que recorremos el código. Cada fila apunta a una categoría del Top Ten y a los capítulos ASVS que verifican ese control.
| # | Pregunta de revisión | Top Ten | ASVS |
|---|---|---|---|
| 1 | ¿Se comprueba que el usuario es dueño del objeto que pide (pedido, factura)? | A01 | V4 (Access Control) |
| 2 | ¿Alguna consulta concatena entrada del usuario en SQL/comandos? | A03 | V5 (Validation/Encoding) |
| 3 | ¿Se renderiza contenido del usuario como HTML sin escapar? | A03 | V5 |
| 4 | ¿Hay secretos embebidos en el código o algoritmos criptográficos débiles? | A02 | V6 (Cryptography) |
| 5 | ¿La app hace peticiones a URLs que controla el usuario? | A10 | V12 (SSRF) |
| 6 | ¿Se accede a ficheros con nombres que vienen del usuario? | A01 | V4, V12 |
| 7 | ¿Faltan cabeceras de seguridad (CSP, HSTS) o helmet? | A05 | V14 (Config) |
| 8 | ¿Las cookies llevan HttpOnly, Secure y SameSite? | A05 | V3 (Session) |
| 9 | ¿Los errores exponen stack traces o detalles internos? | A05 | V7 (Errors/Logging) |
| 10 | ¿Hay rate limiting en autenticación y endpoints sensibles? | A07 | V2 (Authentication) |
El código a revisar: endpoints, componente y configuración
A continuación, el fragmento entregado. Léelo con el checklist en la mano antes de mirar la solución.
config.js (configuración del backend)
// config.js — configuracion del servicio de pedidos
module.exports = {
jwtSecret: 'bazarnube-2021', // usado para firmar los tokens de sesion
jwtAlg: 'HS256',
db: { host: 'db', user: 'app', password: 'app', ssl: false },
cookie: { httpOnly: false, secure: false }, // opciones de la cookie de sesion
};app.js (arranque de Express)
const express = require('express');
const cookieParser = require('cookie-parser');
const app = express();
app.use(express.json());
app.use(cookieParser());
// (no se instala helmet ni se define ninguna Content-Security-Policy)
app.use(require('./routes/orders'));
app.use(require('./routes/catalog'));
// Manejador de errores global
app.use((err, req, res, next) => {
res.status(500).json({ error: err.message, stack: err.stack }); // detalle interno al cliente
});
app.listen(3000);routes/orders.js
const router = require('express').Router();
const db = require('../db');
const path = require('path');
const auth = require('../middleware/auth'); // valida el JWT y pone req.user
// Detalle de un pedido
router.get('/api/orders/:id', auth, async (req, res) => {
const { rows } = await db.query('SELECT * FROM orders WHERE id = $1', [req.params.id]);
res.json(rows[0]); // devuelve el pedido sin comprobar que sea del usuario
});
// Descarga de la factura en PDF
router.get('/api/invoices', auth, (req, res) => {
const file = req.query.file; // ej. ?file=2026-000123.pdf
res.sendFile(path.join('/var/bazarnube/invoices', file));
});
module.exports = router;routes/catalog.js
const router = require('express').Router();
const db = require('../db');
const auth = require('../middleware/auth');
// Buscador de productos
router.get('/api/products/search', async (req, res) => {
const q = req.query.q;
const sql = `SELECT id, name, price FROM products WHERE name LIKE '%${q}%'`;
const { rows } = await db.query(sql); // se concatena q directamente
res.json(rows);
});
// Importar producto: descarga la imagen desde una URL indicada por el vendedor
router.post('/api/products/import', auth, async (req, res) => {
const { imageUrl } = req.body;
const resp = await fetch(imageUrl); // se descarga cualquier URL que envie el usuario
const buffer = Buffer.from(await resp.arrayBuffer());
// ... guarda el buffer como imagen del producto
res.json({ ok: true });
});
module.exports = router;ProductReviews.jsx (componente React)
export function ProductReviews({ reviews }) {
// reviews[].body es texto libre escrito por otros compradores
return (
<ul>
{reviews.map((r) => (
<li key={r.id} dangerouslySetInnerHTML={{ __html: r.body }} />
))}
</ul>
);
}Extracto del informe ZAP (staging)
El escaneo de 06-03 sobre esta rama devolvió, entre otras, estas alertas pasivas y activas:
[High] SQL Injection GET /api/products/search (q) [High] Path Traversal GET /api/invoices (file) [Medium] CSP: Header Not Set / [Low] Cookie No HttpOnly Flag Set-Cookie: session [Low] Application Error Disclosure 500 con stack trace
Cómo documentar un hallazgo y registrarlo en el backlog
Un hallazgo útil es reproducible y accionable. Cada ficha BZN-xxx incluye los mismos campos que ZAP ya nos daba, más el requisito ASVS incumplido:
- ID:
BZN-xxx. - Título: qué es, en una línea.
- Categoría OWASP: A01–A10 del Top Ten 2021.
- Severidad: Crítica/Alta/Media/Baja (por riesgo = impacto x probabilidad).
- Ubicación: fichero/endpoint y línea.
- Evidencia: la petición o el fragmento de código que lo prueba.
- Requisito ASVS: el control que incumple (para el ejercicio 2).
- Cómo se detectó: revisión de código, ZAP, o ambas (mayor confianza).
Ejemplo de ficha bien redactada:
ID: BZN-101
Titulo: IDOR en GET /api/orders/:id (falta control de propiedad)
Categoria: A01 Broken Access Control
Severidad: Alta (acceso a PII y pedidos de otros clientes)
Ubicacion: routes/orders.js, handler GET /api/orders/:id
Evidencia: Autenticado como cliente A, GET /api/orders/778 (pedido de B)
devuelve 200 con los datos de B. No se filtra por req.user.id.
ASVS: V4.1.1 / V4.2.1 (autorizacion a nivel de objeto)
Deteccion: Revision de codigo (ZAP no lo marca: requiere logica de negocio)Nótese el matiz de la última línea: ZAP no detecta el IDOR porque no conoce quién debería poder ver qué. Los fallos de lógica de autorización se cazan casi siempre por revisión de código, mientras que inyección, XSS o path traversal aparecen también en el escaneo dinámico. Por eso el método combina ambas técnicas: ninguna sola es suficiente.
Errores Comunes y Consejos
- Fiarse solo del escáner. ZAP no encuentra IDOR ni secretos embebidos ni criptografía débil. Si tu identificación se limita al informe de ZAP, se te escapará A01 y buena parte de A02. Combina siempre con revisión de código.
- Confundir cantidad con calidad. Reportar cien alertas pasivas triviales entierra las dos críticas de verdad. Prioriza por riesgo real, como en el triaje de 06-03.
- No reproducir antes de reportar. Un hallazgo sin evidencia reproducible se discute eternamente. Adjunta la petición o la línea exacta.
- Ignorar el
dangerouslySetInnerHTML. React escapa por defecto, así que muchos revisores asumen que "no hay XSS". Esa API concreta desactiva la protección: es un imán de A03. - Consejo: revisa siempre los cruces de límite de confianza primero (entrada de usuario, consultas a BD, llamadas a terceros, acceso a ficheros). Ahí vive la inmensa mayoría de los hallazgos, como anticipaba el threat model de 07-02.
Ejercicios
Ejercicio 1. Recorre el fragmento con el checklist y elabora la lista completa de hallazgos: por cada uno, indica endpoint/fichero, categoría del Top Ten 2021 y severidad. Deberías encontrar al menos nueve.
Ejercicio 2. Para el endpoint GET /api/invoices, redacta la ficha BZN-140 completa (todos los campos) e indica el payload concreto que usarías como evidencia.
Ejercicio 3. Justifica por qué el IDOR de pedidos no aparece en el informe de ZAP y qué técnica sí lo detecta. Generaliza: ¿qué categorías del Top Ten se le escapan estructuralmente a un DAST?
Soluciones
Solución 1. Lista de hallazgos del laboratorio:
| ID | Hallazgo | Ubicación | OWASP 2021 | Severidad | Detección |
|---|---|---|---|---|---|
BZN-134 |
SQLi por concatenación de q |
catalog.js /search |
A03 Injection | Crítica | Código + ZAP |
BZN-101 |
IDOR: pedido de otro usuario | orders.js /orders/:id |
A01 Broken Access Control | Alta | Código |
BZN-140 |
Path traversal en file= |
orders.js /invoices |
A01 Broken Access Control | Alta | Código + ZAP |
BZN-131 |
SSRF: descarga de URL arbitraria | catalog.js /import |
A10 SSRF | Alta | Código |
BZN-087 |
XSS almacenado vía dangerouslySetInnerHTML |
ProductReviews.jsx |
A03 Injection | Alta | Código |
BZN-155 |
Secreto JWT embebido y débil | config.js |
A02 Cryptographic Failures | Alta | Código |
BZN-041 |
Sin CSP ni helmet | app.js |
A05 Security Misconfiguration | Media | ZAP |
BZN-039 |
Cookie sin HttpOnly/Secure/SameSite | config.js |
A05 Security Misconfiguration | Media | ZAP |
BZN-060 |
Stack trace expuesto al cliente | app.js (handler de error) |
A05 Security Misconfiguration | Baja | Código + ZAP |
Varios (BZN-140, BZN-131, BZN-087, BZN-041, BZN-039, BZN-060) ya existían en el backlog desde el escaneo de 06-03: este ejercicio los confirma por revisión de código y añade dos nuevos (BZN-134, BZN-101).
Solución 2. Ficha de BZN-140:
ID: BZN-140
Titulo: Path Traversal en GET /api/invoices (parametro file)
Categoria: A01 Broken Access Control
Severidad: Alta (lectura de ficheros arbitrarios del servidor)
Ubicacion: routes/orders.js, handler GET /api/invoices
Evidencia: GET /api/invoices?file=../../../../etc/passwd -> 200 con el fichero
sendFile une el path sin normalizar ni validar que quede dentro de
/var/bazarnube/invoices.
ASVS: V4.1.3 / V12.3.1 (control de acceso a ficheros, path canonizado)
Deteccion: Revision de codigo + alerta activa de ZAP "Path Traversal"El payload de evidencia es ?file=../../../../etc/passwd (o cualquier ruta fuera del directorio de facturas). Confirma la lectura arbitraria.
Solución 3. ZAP no detecta el IDOR porque un DAST prueba entradas y respuestas sin conocer las reglas de negocio: no sabe que el pedido 778 pertenece a otro usuario, así que un 200 le parece correcto. Detectarlo requiere entender la lógica de autorización, lo que se hace por revisión de código o con pruebas manuales autenticadas con dos usuarios. En general, a un DAST se le escapan de forma estructural: A01 (control de acceso/IDOR/escalada), buena parte de A02 (secretos y cripto débil en el código), A04 (fallos de diseño) y A08 (integridad), porque dependen de contexto y lógica, no de patrones observables en la respuesta.
Conclusión
Hemos convertido un fragmento de BazarNube en una lista de nueve hallazgos priorizados, cada uno con su categoría del Top Ten, su severidad, su evidencia y su requisito ASVS incumplido, todos registrados como fichas BZN-xxx en el backlog. Lo importante no es solo la lista, sino el método: contexto, checklist Top Ten/ASVS, revisión de código, escaneo ZAP y correlación, con la lección clave de que ninguna técnica sola basta —el escáner no ve el IDOR, y la revisión manual no escala como el escáner—. Ahora tenemos el diagnóstico; falta la cura. En la siguiente lección, 08-02, tomamos exactamente estas nueve fichas y las remediamos una a una: código corregido, mapeo a requisitos ASVS y re-escaneo para verificar que el control funciona de verdad.
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
