Entorno local, ataques contra TU app. No mires solutions.md hasta entregar.
Ejercicio 1 — SQLi: el f-string que arde
- Escribe (solo en un branch de prueba, para aprender) una vista que filtre eventos con cursor + f-string:
f"... WHERE title LIKE '%{q}%'". Ejecuta conq = "x' OR '1'='1". ¿Qué devolvió? Y conq = "x'; DROP TABLE events_reservation; --"(en una BD de prueba desechable). - Reescríbelo con parámetros
%s: repite los payloads. ¿Qué sale ahora? - Tu
available_seatsusa el ORM: demuestra con un input tipo payload ("'; DROP --comorowde asiento) que nada explota — el ORM parametrizó.
Ejercicio 2 — XSS: del JSON al DOM
- Crea un evento cuyo título sea
<img src=x onerror=alert('xss')>. Consúmelo desde una mini-página HTML con fetch +innerHTML: ¿se ejecuta? (es el riesgo del FRONTEND, no de tu API). - Cambia a
textContent(o el escape del framework): ¿se ejecuta? Registra la regla para tu equipo frontend: "el HTML de usuario jamás va por innerHTML; si hay HTML rico legítimo: sanitizar con DOMPurify server-side o al render". - Cabeceras CSP básicas en tu response (o Nginx):
Content-Security-Policy: default-src 'self'— ¿qué bloquea esto del escenario anterior como segunda capa?
Ejercicio 3 — SSRF: el guard
- Añade (branch de prueba) un endpoint
POST /api/tools/fetch/ {"url":...}que devuelve el título de la URL. Atácalo:http://localhost:6379/yhttp://169.254.169.254/latest/meta-data/(local no tiene metadata real, pero la técnica es la del ataque a nube). - Implementa el guard: esquema http/https only, resolve con
socket.getaddrinfoy rechaza IPs privadas/loopback/link-local (bloquear el rango 169.254.0.0/16, 127.0.0.0/8, 10/8, 172.16/12, 192.168/16), y whitelist de dominios si el caso lo permite. - ¿Por qué la resolución DNS debe hacerse ANTES del fetch y contra la IP a la que conectarás (DNS rebinding)?
Ejercicio 4 — check --deploy y el browseable API
- Corre
python manage.py check --deploy: lista los warnings que te da hoy. - Configura el bloque de settings seguro de la lección (HSTS, cookies seguras, nosniff, referrer, X-Frame-Options DENY) y vuelve a correr: ¿queda verde?
- Apaga el browseable API en producción (
DEFAULT_RENDERER_CLASSESsolo JSON) y justifica qué exponía.
Ejercicio 5 — Componentes y dependencias
pip install pip-audity correpip-auditsobre tu requirements: ¿cuántas vulnerabilidades conocidas hay en tus versiones? Anota las 2 más graves.- Fija versiones exactas (
==) en requirements y añade pip-audit a tu "pipeline local" (script). ¿Por qué el pinning + audit es la pareja mínima de A06?
Ejercicio 11 — Los tres del §11, cazados
- Mass assignment: crea el endpoint vulnerable a propósito (
fields = "__all__"en el serializer de perfil) y demuéstralo: PATCH del perfil con"is_staff": true→ ¿el usuario se auto-escaló? Fix: serializer explícito + test de regresión que envía el campo y verifica que no llegó. - Timing: compara
secret == adivinanzavshmac.compare_digestcon 1000 mediciones (el timeit del 37): ¿la diferencia de tiempo revela caracteres correctos en la primera? Pega los números. - Dependencias: introduce un typo en una dependencia de test (
pip install requetsen un venv de laboratorio): ¿qué instala? Después: pinnea tus dependencias con hashes (pip-compile --generate-hashes) y correpip-audit— el reporte del 41.
Ejercicio 12 — security.txt y el runbook
- Escribe tu
/.well-known/security.txt(contacto, expiración, política de divulgación, encriptación para reportes) y el test que verifica que existe y expira a futuro. - El runbook del hallazgo (§12) aplicado a una vulnerabilidad simulada ("reporte externo: el endpoint de eventos filtra emails en el listado de reservas"): los 5 pasos con comandos reales (¿cómo contienes? ¿qué test de regresión? ¿notificas?). Máx 1 página (48).
- La pregunta raíz del postmortem: ¿qué PIEZA del curso (review del 50, pipeline del 41, tests del 34) habría cazado ese filtrado? Si la respuesta es "ninguna": esa es la acción P1.
Entrega
Pega outputs de los ataques (¡en tu entorno!) y los guards implementados. Después: Lección 23 — Secretos, rate limiting y RGPD.