Material del profesor. Entrega al alumnado la guía y el laboratorio; reserva este archivo si utilizas el reto como prueba de comprensión.
Chrome es la referencia de las explicaciones por la organización conjunta de depuración, red, almacenamiento y overrides. Firefox sirve para verificar que los conceptos se trasladan entre navegadores y para practicar el editor de peticiones. Evita presentar capacidades de seguridad como exclusivas de uno: Chrome permite reenviar como fetch y Firefox reciente dispone de overrides.
Antes de clase prueba ambos puertos, login, catálogo, la marca XSS y la página CSRF en la imagen real del aula. Un navegador administrado puede restringir herramientas o carpetas de overrides. Las capturas del alumnado deben mostrar versión y variante de la página cuando eso afecte al resultado.
Muestra el precio 25, cambia solo el texto a 1 y pregunta qué cree el grupo que sucederá al comprar. Envía: el total sigue siendo 25. Cambia después el campo oculto: en la variante vulnerable el total pasa a 1. Repite con la API segura: vuelve a 25. Esta secuencia permite introducir tres capas distintas con una evidencia visible en cada una.
Después revela el botón de administración. Su respuesta 403 contrasta con el fallo de precio: la aplicación incluye controles que funcionan y otros que fallan. No se busca que el alumnado marque todo como vulnerable, sino que justifique cada conclusión.
| Práctica | Resultado que debe observarse | Idea que debe explicar el alumno |
|---|---|---|
| P1 DOM y CSS | Cambios locales; admin 403 con sesión; restauración tras recarga | Una representación local no es una modificación de servidor |
| P2 Consola | dataset produce cadena; fetch genera una petición | El contexto es el de la página; HTTP 403 no rechaza fetch por sí solo |
| P3 HTTP | GET de catálogo, POST de eco, JSON y retraso aproximado de 800 ms | URL, cabeceras y cuerpo son partes distintas |
| P4 Depuración | a y b son cadenas; resultado 23; conversión da 5 | El tipo de dato explica el comportamiento |
| P5 Validación | La interfaz impide cantidad 0; petición directa recibe 400 | Cliente y servidor validan en capas distintas |
| P6 Compra | Precio enviado 1 y cantidad 2 da 2 en vulnerable y 50 en seguro | El precio debe obtenerse del catálogo de servidor |
| P7 Almacenamiento | rol_demo cambia; rol servidor no; sesion no aparece en document.cookie | HttpOnly limita APIs de página, no al dueño de DevTools |
| P8 IDOR | 1002 devuelve objeto ajeno en vulnerable; 403 en seguro | Sesión existente no implica permiso sobre todos los objetos |
| P9 XSS | innerHTML ejecuta onerror; textContent muestra entrada literal | Debe identificar fuente, destino e interpretación |
| P10 CSP | CSP bloquea manejador; Report Only lo permite; marco protegido no carga | Una política de observación no aplica bloqueo |
| P11 CORS | Lectura denegada o autorizada; preflight; contador sube pese al error | Hay que distinguir envío de petición y lectura de respuesta |
| P12 CSRF | Formulario externo cambia tema; variante protegida 403; legítima 200 | Origen, token de sesión y contexto de site tienen funciones distintas |
| P13 Exposición | Configuración ficticia visible en JS y API | La gravedad depende de qué permite hacer el valor |
| P14 Overrides | Cambia catálogo local; compra segura conserva precio real | Sustituir una respuesta local no altera el backend |
| P15 Calidad | Error al desconectar, tarea acotada de 250 ms, revisión de etiquetas | Medir y observar antes de proponer una mejora |
Ejecuta desde la consola de http://localhost:8000, después de iniciar sesión. Los ejemplos no necesitan construir manualmente Cookie. El navegador la adjunta cuando procede.
fetch('/api/compra', {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify({producto: 1, cantidad: 2, precio: 1})
}).then(async r => console.log(r.status, await r.json()));Se espera 200, cantidad 2, total 2 y simulacion true. Con
?modo=seguro, el mismo cuerpo devuelve total 50. Cantidad
0, 11, 1.5 o la cadena “2” devuelve 400 en las dos variantes. El fallo
de precio es independiente de esa validación.
Promise.all([
'/api/pedido?id=1001',
'/api/pedido?id=1002',
'/api/pedido?id=1001&modo=seguro',
'/api/pedido?id=1002&modo=seguro',
'/api/admin'
].map(async url => {
const r = await fetch(url);
return {url, estado: r.status, respuesta: await r.json()};
})).then(console.table);Estados esperados con sesión: 200, 200, 200, 403 y 403. Sin sesión los pedidos y admin responden 401.
Entrada de referencia:
<img src="/imagen-inexistente" onerror="document.getElementById('marca').textContent='XSS demostrado en el laboratorio'">Prueba la misma entrada en cuatro navegaciones desde cero:
/ con q: cambia la marca./ con q y modo=seguro: entrada literal; marca
inicial./csp.html con q: imagen insertada, manejador bloqueado
y marca inicial./reporte.html con q: infracción informativa y marca
modificada.No conviertas el botón Mostrar entrada como texto en una supuesta limpieza de toda la sesión: elimina el marcado del saludo, pero una ejecución anterior ya ha ocurrido. Para la comprobación de corrección hay que volver a cargar con modo=seguro.
Los puertos 8000 y 8001 son orígenes distintos. Son same-site en este montaje y comparten el ámbito de host de las cookies. La prueba de preferencia es un escenario entre orígenes del mismo site. No afirmes que demuestra que SameSite=Lax falla siempre frente a un dominio atacante externo.
La política de seguridad de navegador exige precisión en estas palabras. Un alumno que detecte esta limitación y la incluya en su informe ha entendido mejor el ejercicio que quien lo denomine simplemente ataque desde otro dominio.
HttpOnly no oculta el valor al propietario del navegador. Evita que las APIs JavaScript de la página lo lean directamente. El navegador sigue enviando la cookie cuando corresponda. El XSS del laboratorio modifica el DOM sin extraer cookies para que la prueba no mezcle dos capacidades distintas.
El contador es la evidencia de que el POST se procesó aunque el script no pudo leer la respuesta. No basta con mirar el error rojo. Para un POST JSON con cabecera personalizada, el navegador realiza primero OPTIONS; si se rechaza ese permiso, no envía el POST posterior. Son dos recorridos distintos y ambos deben quedar claros.
El token CSRF se vincula a la sesión del servidor; reiniciar Python la invalida. Volver a iniciar sesión establece una nueva cookie. Local Storage permanece en el navegador y no cambia el rol real. Un override o una conexión en modo Offline pueden interferir en prácticas posteriores.
El programa utiliza estado compartido en memoria y es un servidor didáctico de un equipo, no una solución multiusuario de aula. No hay base de datos, pedidos persistidos, pasarela de pago ni cuentas administrativas reales. Todo el impacto debe describirse con ese alcance.
Un hallazgo se considera confirmado si incluye una precondición, una petición o entrada concreta, un resultado observable y una explicación que no excede la evidencia. Para cada corrección pide al menos un caso permitido y otro que antes provocaba el problema. Una corrección que rompe toda la funcionalidad no demuestra por sí sola una solución adecuada.
Ejemplos de controles que el alumno puede acreditar:
No aceptes como impacto demostrado “control total del servidor”, “robo de dinero” o “acceso al disco” a partir de estas pruebas. Tampoco aceptes “todo es seguro” por una CSP o una cookie HttpOnly.
Si dispones de dos sesiones de tres horas, conserva HTTP, depuración, compra, cookies, IDOR y XSS. Muestra CSP como comparación final. Conviene separar CORS y CSRF para una ampliación con tiempo suficiente. Si solo tienes noventa minutos, utiliza la demostración inicial de precio, la petición en Network y la comparación del pedido seguro; no intentes cubrir todas las siglas.
Como actividad posterior puedes pedir manejo explícito de errores en api, indicador de carga, bloqueo temporal del botón de compra y una estrategia de reintento que no duplique operaciones. Para evolucionar el backend harían falta persistencia, gestión real de usuarios, sesiones con caducidad, despliegue HTTPS, límites de uso y otros requisitos propios del proyecto. Estos cambios quedan fuera de la simulación actual y deben diseñarse según el entorno de destino.