From n00b to ZeroCool / El lado oscuro
OWASP Top 10 sin humo: los pecados capitales que ya tumbaron apps
Guía defensiva del OWASP Top 10 con ejemplos reales, checks rápidos y pasos para bajar riesgo sin romper production.
Lo que vale la pena leer aquí
Eran las 11:47 pm. El deploy ya iba tarde. En Slack alguien soltó: “¿por qué el WAF está bloqueando el login?” y otro contestó “seguro es falso positivo”.
Eran las 11:47 pm. El deploy ya iba tarde. En Slack alguien soltó: “¿por qué el WAF está bloqueando el login?” y otro contestó “seguro es falso positivo”.
Spoiler: no era falso positivo.
Era un bug de autorización que llevaba meses ahí, bien guardado entre “luego lo vemos” y un par de pull requests apurados. Eso no truena hasta que truena… y normalmente revienta cuando ya estás en production y traes café frío.
La neta: el OWASP Top 10 no es teoría para examen ni checklist de compliance para quedar bien. Es la lista de las formas más comunes en las que una app web se mete el pie, filtra datos o termina en un incidente que te toca explicar.
Qué te vas a llevar
- Qué es el OWASP Top 10 en términos de riesgo real (no de “cumplimos y ya”).
- Cómo se ven estos pecados en apps típicas: login, dashboard, APIs, SaaS multi-tenant.
- Un método defensivo para detectarlos y priorizarlos sin perder una semana entera.
- Acciones aterrizadas: qué revisar en código, config y pipeline.
- Errores muy de jale en MX/LatAm (sí, el “ahorita lo parchamos”).
Por qué esto pega en la vida real
El OWASP Top 10 es un ranking de categorías de riesgo frecuentes en apps web. No es “la lista completa de hacks”; es más bien un mapa de dónde se rompe la seguridad cuando el equipo está ocupado sacando features.
Y duele por razones bien mundanas:
- deadlines con code review de 5 minutos,
- dependencias que nadie toca porque “si funciona no lo muevas”,
- setups copiados de un tutorial,
- y el clásico: “en staging sí jala”.
Dos escenas que se repiten:
-
Startup con runway corto: el founder pide “súbelo ya, mañana vemos seguridad”. Mañana llega con un ticket: un cliente ve datos de otro.
-
Empresa grande con proceso: hay scanner, hay auditoría, hay “gobernanza”… pero el login sigue sin rate limit porque “eso es de infraestructura”, y el equipo del API no tiene permisos para tocar el gateway.
Guía principal: OWASP Top 10 (2021) con mentalidad defensiva
Van las 10 categorías del OWASP Top 10 explicadas como se viven: cómo se manifiestan y qué puedes hacer desde dev, QA, sysadmin o soporte para bajar riesgo.
A01: Broken Access Control (Control de acceso roto)
Cómo se ve:
- Cambias un ID en la URL y ves info de otro usuario (IDOR).
- Un usuario sin rol admin puede pegarle a endpoints “admin-only”.
- Multi-tenant mal armado:
tenant_idviene del cliente y el server “confía”.
Decisión práctica: si tu app tiene usuarios, roles o tenants, esto casi siempre es prioridad #1.
Acciones defensivas:
- Autorización server-side en cada request. No en el frontend. No “porque el botón no aparece”.
- Usa un patrón claro tipo
can(user, action, resource)y aplícalo parejo. - Multi-tenant: el
tenantdebe salir del contexto autenticado (token/session), no del body. - En tests: mete casos negativos (usuario A intentando leer recurso de B).
Check rápido: busca endpoints que acepten userId, accountId, tenantId y valida si el backend comprueba pertenencia.
A02: Cryptographic Failures (Fallos criptográficos)
Cómo se ve:
- Passwords con hashing débil o sin sal.
- Datos sensibles en texto plano en logs (tokens, números, CURP, etc.).
- TLS mal configurado o tráfico sensible sin HTTPS.
Decisión práctica: no necesitas ser cripto-nerd; necesitas evitar errores de principiante.
Acciones defensivas:
- Passwords: usa bcrypt/Argon2 (según tu stack) con parámetros razonables.
- En tránsito: fuerza HTTPS, activa HSTS y redirige HTTP → HTTPS.
- En reposo: cifra secretos y datos sensibles donde aplique (y define qué cuenta como “sensible”).
- Logs: scrub/masking de tokens y PII. Un log mal puesto termina en un bucket/Blob y luego en incidente.
Tradeoff real: cifrar “todo por default” suena bonito, pero te rompe búsquedas y debugging. Empieza por credenciales, tokens, PII y datos financieros; deja una política clara.
A03: Injection (Inyección)
Cómo se ve:
- SQL injection por concatenar strings.
- NoSQL injection por armar queries con inputs crudos.
- Command injection en scripts internos que reciben parámetros.
Decisión práctica: si hay input de usuario, asume que lo van a usar para intentar romper tu parser.
Acciones defensivas:
- DB: queries parametrizados siempre.
- Validación/normalización: valida tipo, rango y formato en backend.
- Para búsquedas: define un set de operadores explícitos; no “pasa lo que mande el cliente”.
- Límite de daño: usuario de DB con permisos mínimos (no
rooten production).
Check rápido: cualquier "SELECT ... " + userInput es alarma.
A04: Insecure Design (Diseño inseguro)
Cómo se ve:
- Flujos peligrosos por diseño (transferencias sin confirmación, sin límites, sin antifraude).
- Feature nueva con suposiciones optimistas (“nadie va a intentar eso”).
Decisión práctica: aquí decides si vas a vivir parchando o si cierras el agujero de raíz.
Acciones defensivas:
- Threat modeling ligero: activos, actores, superficie, abusos.
- Controles por escenario: rate limit, límites, verificación adicional, auditoría.
- Safe defaults: lo nuevo nace cerrado, no abierto.
Momento de guerra: el bug más caro que he visto no era un input mal sanitizado, era un flujo de negocio: cancelar suscripción y quedarte con beneficios por un bug de estado. Nadie lo etiquetó como “seguridad” hasta que hubo abuso masivo.
A05: Security Misconfiguration (Mala configuración)
Cómo se ve:
- Headers de seguridad ausentes.
- Admin panels expuestos.
- Storage público “sin querer”.
- Debug mode en producción.
Decisión práctica: si tu app “jala” pero no tienes un baseline de configuración, estás jugando a la ruleta.
Acciones defensivas:
- Baseline de headers: CSP (práctica y gradual), X-Content-Type-Options, Referrer-Policy, etc.
- Apaga listados, endpoints de debug, “/actuator” o equivalentes.
- Infra as code: configura por código, no por clicks de último minuto.
- Revisa permisos de buckets y secretos en CI/CD.
Check rápido: corre un escaneo ligero de config y compáralo contra un estándar interno.

A06: Vulnerable and Outdated Components (Componentes vulnerables/desactualizados)
Cómo se ve:
- Dependencias con CVEs conocidas.
- Images de Docker con paquetes viejos.
- Librerías abandonadas que ya nadie mantiene.
Decisión práctica: no es actualizar “por deporte”, es administrar riesgo.
Acciones defensivas:
- SBOM o mínimo un inventario automático.
- Alertas: Dependabot/Renovate + política de merge.
- Regla simple: “CVSS alto + reachable = hotfix”; lo demás al backlog.
- Congela versiones, pero con rutina de upgrades (mensual/quincenal según producto).
Realidad de oficina: cuando la laptop ya está vieja y el build tarda 20 minutos, la banda evita upgrades. Mete cache de paquetes, mirrors internos y un pipeline que no castigue.
A07: Identification and Authentication Failures (Identificación/autenticación)
Cómo se ve:
- Sessions que no expiran.
- Tokens eternos.
- Password reset inseguro.
- MFA inexistente para cuentas privilegiadas.
Decisión práctica: auth no es “un feature”, es el cinturón de seguridad.
Acciones defensivas:
- MFA para admin y cuentas de alto impacto.
- Rotación/expiración razonable de sesiones/tokens.
- Protege flows: rate limit en login/reset; mensajes de error que no revelen si existe el usuario.
- Cookies/sesiones bien: HttpOnly, Secure, SameSite razonable.
A08: Software and Data Integrity Failures (Integridad de software y datos)
Cómo se ve:
- CI/CD sin controles: cualquiera puede colar un artifact.
- Updates o plugins sin firma/verificación.
- Dependencias desde fuentes no confiables.
Decisión práctica: si se rompe tu supply chain, da igual qué tan bonito esté tu código.
Acciones defensivas:
- CI con permisos mínimos y secrets bien aislados.
- Firma/verificación de artifacts cuando aplique.
- Bloquea merges sin reviews/CI verde.
- Protege ramas y habilita 2FA en repos.
Tradeoff real: endurecer el pipeline puede hacer más lento el deploy. Compénsalo con automatización: checks rápidos y en paralelo.
A09: Security Logging and Monitoring Failures (Fallos de logging/monitoreo)
Cómo se ve:
- No hay trazas para investigar.
- Alertas inexistentes o puro ruido.
- Nadie revisa logs hasta que ya tronó.
Decisión práctica: incidentes va a haber. La pregunta es si te enteras por monitoreo o por un tweet.
Acciones defensivas:
- Define eventos de seguridad: login fallido repetido, cambios de rol, accesos denegados, reset de password, exportaciones.
- Centraliza logs y correlación: request id, user id, tenant id.
- Retención y acceso: logs protegidos, con auditoría.
- Playbooks: qué hacer cuando salta una alerta.
A10: Server-Side Request Forgery (SSRF)
Cómo se ve:
- La app hace requests a URLs que manda el usuario (webhooks, importadores, previews).
- Integraciones “convenientes” que terminan pegándole a recursos internos.
Decisión práctica: cada feature que “consume una URL” es sospechoso.
Acciones defensivas:
- Allowlist de dominios/hosts. No “cualquier URL”.
- Resuelve DNS y valida IPs contra rangos privados/loopback.
- Timeouts cortos, límites de redirects.
- Separa redes: servicios internos no deberían ser alcanzables desde el componente que procesa URLs si no es necesario.
Paso a paso: mini-programa OWASP en 7 días (realista)
Pensado para que una persona o un equipo pequeño lo saque sin detener el roadmap.
Día 1: Inventario y mapa de superficie (2–3 horas)
- Lista endpoints públicos, paneles de admin, integraciones (webhooks, import/export, login, reset).
- Identifica activos: datos personales, pagos, API keys, archivos.
- Define rutas críticas: login, cambios de email, permisos, facturación.
Output: un doc simple (o un ticket épico) con superficies + responsables.
Día 2: Prioriza por riesgo y probabilidad (1–2 horas)
- Prioridad máxima: Broken Access Control, Auth, Injection, Misconfig.
- Marca dónde hay multi-tenant, roles y endpoints sensibles.
Output: Top 5 puntos a corregir (no Top 50).
Día 3: Endurece autenticación y sesiones (medio día)
- MFA para admin.
- Expiración y rotación razonable.
- Rate limiting en login/reset (a nivel gateway si se puede).
Output: cambios desplegables sin tocar mucho negocio.
Día 4: Revisión de autorización (1 día)
- Recorre endpoints críticos y verifica checks server-side.
- Agrega pruebas automatizadas de acceso (mínimo 5–10 casos negativos).
Output: PR con guardrails + tests.
Día 5: Dependencias y contenedores (2–4 horas)
- Corre scanner de dependencias.
- Actualiza lo crítico y reachable.
- Asegura base image actualizada.
Output: PR de upgrades con nota de impacto.
Día 6: Config y headers (2–4 horas)
- Desactiva debug.
- Revisa storage público.
- Agrega headers básicos y CSP gradual.
Output: baseline de configuración versionado.
Día 7: Logging + alertas accionables (medio día)
- Centraliza eventos clave.
- Agrega 3 alertas: picos de login fallido, cambios de rol, exports masivos.
- Documenta un playbook mini.
Output: cuando algo raro pase, te enteras y puedes investigar.
Screenshots sugeridos
- Captura del pipeline (GitHub Actions/GitLab CI) mostrando job de dependencias/secret scanning.
- Config de headers de seguridad (Nginx/Cloudflare/ALB) con un diff.
- Dashboard de monitoreo (Grafana/Datadog) con métrica de login failures y alertas.
- Ejemplo de test automatizado de autorización (suite pasando y caso negativo).
Errores comunes + solución (de los que sí duelen)
1) “El frontend oculta el botón, entonces ya está protegido”
Problema: el backend sigue aceptando el request.
Solución: autorización en servidor por endpoint + tests negativos.
2) “Le metimos JWT, ya somos seguros”
Problema: tokens eternos, sin revocación, sin claims bien validados.
Solución: expiración corta, refresh con rotación, validación estricta de issuer/audience y manejo de revocación cuando aplique.
3) “Actualizar dependencias rompe todo, mejor no”
Problema: acumulas deuda y un CVE crítico te obliga a un upgrade de pánico.
Solución: rutina de upgrades + PRs pequeños y frecuentes + policy por severidad.
4) “Logs para todo” (y terminas loggeando secretos)
Problema: exfiltras tokens/PII a tu propio sistema de logs.
Solución: redacción/masking, niveles de logging y revisión cuando alguien cambia logging.
5) “Rate limit luego, no hay tiempo”
Problema: te cae credential stuffing o te revientan el costo por requests.
Solución: rate limit en gateway por ruta/usuario/IP, con excepciones controladas para integraciones.

Checklist final (para pegarlo en tu board)
- Autorización server-side en rutas críticas (incluye multi-tenant)
- Tests negativos de acceso (al menos para 5 endpoints sensibles)
- Password hashing correcto + políticas mínimas
- Sesiones/tokens con expiración y rotación; MFA para admin
- Queries parametrizados; validación backend de inputs
- Headers de seguridad y debug desactivado en producción
- Dependencias escaneadas y plan de upgrades (no “algún día”)
- Secrets fuera del repo y bien aislados en CI/CD
- Logs sin PII/tokens; alertas accionables configuradas
- Revisión de features que consumen URLs (mitigación SSRF)
FAQ
1) ¿Aplica si mi app está “chiquita”?
Sí. Y a veces pega más: menos tiempo, menos ojos, más copypaste. Los atacantes no discriminan por tamaño; discriminan por superficie expuesta.
2) ¿Qué arreglo primero si solo tengo un sprint?
Broken Access Control y Authentication. Si alguien puede ver/editar lo que no debe o tomar cuentas, lo demás pasa a segundo plano.
3) ¿Necesito herramientas caras para cubrir esto?
No necesariamente. Con linters, SAST básico, escaneo de dependencias, un buen gateway/rate limit y tests bien pensados cubres mucho. Lo caro es no tener workflow.
4) ¿Cómo evito que seguridad se vuelva “policía” y frene al equipo?
Con guardrails automáticos: templates, libs internas, pipelines que den feedback rápido y reglas claras de severidad. Seguridad como default, no como regaño.
5) ¿Qué le digo a negocio cuando pida “solo shipéalo”?
Habla en riesgo y costo: “Si sale así, podemos filtrar datos entre clientes o nos pueden tumbar cuentas. Dame 1 día para meter estos controles y evitamos un incidente que nos pare el mes”.
Siguiente episodio
La lista ayuda, pero el upgrade real es cambiar el chip: de “parchar bugs” a diseñar con amenazas en mente.
El que sigue: threat modeling sin ceremonia: cómo hacerlo en 45 minutos antes del deploy.
Idea para cerrar bien este post: toma una sola práctica de aquí y conviértela en algo que tu equipo pueda aplicar hoy.
Cuando un artículo aterriza en decisiones reales, deja de ser contenido y se vuelve ventaja.


