Módulo 4 · Autenticación y seguridad

Lección 22 — OWASP Top 10

Inyección SQL, XSS, CSRF, SSRF y control de acceso roto — con ejemplos reales.

Publicada
En esta lección
  1. Objetivos
  2. 1. A01 — Broken Access Control (el #1 real)
  3. 2. A02 — Cryptographic Failures
  4. 3. A03 — Injection (SQL, comandos, plantillas)
  5. 4. A04 — Insecure Design / A05 — Security Misconfiguration
  6. 5. A07 — Identification/Auth failures + A10 SSRF
  7. 6. El inventario de TicketFlow (ejercicio)
  8. 11. Más allá del Top 10: los tres ataques que el ranking no encabeza y sí muerden
  9. 12. La respuesta a la vulnerabilidad: el runbook del hallazgo

Stack: Django/DRF · Proyecto: TicketFlow Estado: Publicada Prerrequisito: Lección 21 — Autorización


Objetivos

  1. Recorrer el OWASP Top 10 (2021) con el ataque concreto y la defensa concreta en tu stack.
  2. Encontrar y cerrar las debilidades que tu propio proyecto ya tiene (inventario honesto).
  3. Aprender el triaje: qué arreglas hoy, qué vigila el framework, qué audita un pentest.

1. A01 — Broken Access Control (el #1 real)

El agujero de la 21 (IDOR, escalada de rol, reglas solo en frontend). Defensas ya implementadas: queryset filtrado, permissions de objeto, reglas en el servicio, tests de regresión de escalada. El check que falta SIEMPRE: revisar cada endpoint nuevo con la matriz de la 21 — el OWASP no se "arregla una vez", es una disciplina por-PR.

2. A02 — Cryptographic Failures

Datos sensibles sin cifrar o con cifrado débil:

  • En tránsito: TLS obligatorio (HSTS de la 01), nada sensible por HTTP.
  • En reposo: hashes de contraseña con argon2 (20); secretos TOTP cifrados en reposo (20); tokens de reset hasheados.
  • Cifrado de campo: django-fernet-fields/field custom con clave de settings/KMS (23) para lo que la BD no debe leer (documento de identidad del comprador si lo pidiera el negocio).
  • Nunca: inventar "cifrado" propio, MD5/SHA1 para nada de seguridad, contraseñas reversibles.

3. A03 — Injection (SQL, comandos, plantillas)

  • SQLi: el ORM parametriza SIEMPRE. El agujero aparece con .raw(), extra() o cursores con f-strings: cursor.execute(f"SELECT... {user_input}") = inyección servida. Regla: parámetros %s siempre; si el SQL dinámico es inevitable (orden por columna), whitelist de identificadores (13).
  • Comandos: os.system("convert " + filename) — jamás: subprocess.run([...], shell=False) con lista, o mejor: librerías en lugar de shell.
  • Template injection: renderizar input de usuario con Template(...) ejecuta código. Django templates auto-escapan; el riesgo es usar el input como plantilla.
  • XSS (encaja aquí en DRF): tu API devuelve JSON y el frontend escapa al renderizar (React/Vue lo hacen por defecto) — el agujero aparece con dangerouslySetInnerHTML/v-html con datos de usuario (el nombre del evento con <script>: escapar o sanitizar con DOMPurify si hay HTML rico legítimo).

4. A04 — Insecure Design / A05 — Security Misconfiguration

Diseño: las invariantes en BD (00b/10) y la matriz de autorización (21) SON seguridad de diseño: el OWASP premia prevenir en el esquema antes que parchear en la vista. Misconfiguración — el checklist de Django:

python
DEBUG = False                        # el stacktrace filtra settings y rutas
ALLOWED_HOSTS = ["ticketflow.app"]   # evita host header poisoning
SECURE_SSL_REDIRECT, SECURE_HSTS_SECONDS = True, 31536000
SESSION_COOKIE_SECURE, CSRF_COOKIE_SECURE = True, True
SECURE_CONTENT_TYPE_NOSNIFF, SECURE_REFERRER_POLICY = True, "same-origin"
X_FRAME_OPTIONS = "DENY"             # clickjacking

python manage.py check --deploy lo audita — entra al pipeline (41). Y los errores por defecto de DRF: browseable API apagado en producción (renderer JSON solo) para no exponer formularios de prueba.

5. A07 — Identification/Auth failures + A10 SSRF

Auth: credential stuffing (rate limit 20/23), tokens eternos (18), resets flojos (20) — todo cubierto. SSRF (Server-Side Request Forgery): tu backend hace fetch a URLs dadas por usuarios (p. ej. "avatar_url" del organizador) y un atacante pide http://169.254.169.254/ (metadata de la nube = credenciales) o http://localhost:6379/ (tu Redis). Defensa: whitelist de dominios/esquemas, resolver y bloquear IPs privadas, nunca fetch ciego de URLs de usuario.

El resto (A06 componentes vulnerables: pip-audit en CI; A08 integridad de despliegue: firmas de imágenes/dependencias pinneadas; A09 logging/monitoring: la 45/46) se cubre en sus módulos — el OWASP es un mapa, no un sprint.

6. El inventario de TicketFlow (ejercicio)

Tu proyecto ya tiene: ORM parametrizado, argon2, HMAC en webhooks, rate limit, autorización en capas, constraints como invariantes. Sus agujeros típicos pendientes: check --deploy sin pasar, browseable API activo, alguna URL de avatar/imagen de usuario (SSRF latente), logs sin sanitizar de payloads. Los ejercicios los convierten en tickets.

11. Más allá del Top 10: los tres ataques que el ranking no encabeza y sí muerden

El Top 10 es un ranking de CATEGORÍAS; los ataques concretos del día a día del backend: (1) Mass assignment: el cliente envía is_staff: true en el JSON del perfil y el serializer lo asigna — la cura: serializers explícitos (solo los campos del contrato, 15) jamás fields = "__all__" (la 21 lo completa con permisos por campo); (2) Timing attacks: la comparación de secretos con == filtra por tiempo — la cura: hmac.compare_digest SIEMPRE (ya la usaste en la 17/27) y respuestas de reset con timing uniforme (la 20); (3) Dependency confusion / typosquatting: el pip install requests de un typo instala el paquete del atacante — la cura: lockfile con hashes (40), proxy privado para el nombrado interno, y el pip-audit del 41.

12. La respuesta a la vulnerabilidad: el runbook del hallazgo

Encontrar (o que te reporten) una vulnerabilidad es el inicio del proceso, no el final: (1) triage en <24 h: explotable en TU contexto (el triage de CVE del 40), severidad, quién decide; (2) contención si está siendo explotada: el flag/rollback del 41, el rate limit del 23, la revocación de secretos (27); (3) fix + test de regresión (33: el test que reproduce el exploit, ahora verde); (4) divulgación responsable: si afecta a usuarios (brecha, 56): el runbook de notificación 72 h; si no: el changelog discreto; (5) el postmortem (47) con la pregunta raíz: ¿por qué el review/pipeline no lo cazó? — y la acción que lo cazará la próxima.

El programa de recompensas (security.txt + correo de seguridad) convierte el reporte ajeno en regalo: la página /.well-known/security.txt de TicketFlow con contacto, política de divulgación y PGP — 20 líneas que el pentester honesto busca antes de reportar a un foro.


  1. ¿Por qué el OWASP #1 es control de acceso y no inyección? ¿Qué disciplina lo sostiene?
  2. Explica a un junior por qué cursor.execute(f"... {email}") es grave y execute("... %s", [email]) no.
  3. Tu SPA muestra event.description que el organizador escribe con HTML: ¿dónde está el riesgo de XSS y qué decisión tomas (escapar vs sanitizar)?
  4. ¿Por qué SSRF apunta a 169.254.169.254 y qué obtiene el atacante si lo logras?
  5. ¿Qué tres checks de check --deploy fallarían en tu proyecto hoy?

Continúa con los ejercicios. Las solutions.md solo tras intentarlo.