OWASP Top 10 (2025): las vulnerabilidades que todo desarrollador debe conocer
OWASP Top 10 es el ranking de referencia de las vulnerabilidades más críticas en aplicaciones web. La versión 2025 — publicada en noviembre de ese año — introduce dos categorías nuevas (Software Supply Chain Failures y Mishandling of Exceptional Conditions), absorbe SSRF dentro de Broken Access Control y reorganiza el orden para reflejar los datos reales de más de 500.000 aplicaciones analizadas. Esta guía recorre las 10 categorías con código vulnerable, corrección técnica y herramientas de detección.
Mapa completo: 2021 → 2025
| 2025 | Categoría | Cambio desde 2021 |
|---|---|---|
| A01 | Broken Access Control | Se mantiene #1. Absorbe SSRF (antes A10) |
| A02 | Security Misconfiguration | Sube de #5 a #2 |
| A03 | Software Supply Chain Failures | NUEVA — expande la antigua A06 |
| A04 | Cryptographic Failures | Baja de #2 a #4 |
| A05 | Injection | Baja de #3 a #5 |
| A06 | Insecure Design | Era A04 en 2021 |
| A07 | Authentication Failures | Renombrada (antes "Identification and...") |
| A08 | Software or Data Integrity Failures | Se mantiene |
| A09 | Security Logging and Alerting Failures | Renombrada (añade "Alerting") |
| A10 | Mishandling of Exceptional Conditions | NUEVA — 24 CWEs, 769.581 ocurrencias |
A01: Broken Access Control
Número 1 en 2021 y de nuevo en 2025. Ocurre cuando un usuario accede a recursos o ejecuta acciones para las que no tiene autorización. En 2025 absorbe SSRF (que antes era A10), ya que SSRF es una forma de acceso no autorizado a recursos internos desde el servidor.
Ejemplo vulnerable (IDOR)
// Devuelve datos de cualquier usuario sin verificar permiso
app.get("/api/users/:id", async (req, res) => {
const user = await db.users.findById(req.params.id);
res.json(user);
});Corrección
app.get("/api/users/:id", authenticate, async (req, res) => {
if (req.user.id !== req.params.id && req.user.role !== "admin") {
return res.status(403).json({ error: "Forbidden" });
}
const user = await db.users.findById(req.params.id);
res.json(user);
});SSRF (absorbido aquí en 2025)
// ❌ El usuario controla la URL — puede apuntar a 169.254.169.254
app.post("/api/fetch", async (req, res) => {
const response = await fetch(req.body.url);
res.send(await response.text());
});
// ✅ Allowlist + bloqueo de IPs internas
const ALLOWED = ["api.ejemplo.com"];
app.post("/api/fetch", async (req, res) => {
const url = new URL(req.body.url);
if (!ALLOWED.includes(url.hostname)) return res.status(400).end();
const resolved = await dns.resolve4(url.hostname);
if (resolved.some(isPrivateIP)) return res.status(400).end();
const response = await fetch(req.body.url);
res.send(await response.text());
});A02: Security Misconfiguration
Sube del puesto 5 al 2. Configuraciones por defecto inseguras, stack traces en producción, headers ausentes, permisos de S3 públicos, credenciales por defecto no cambiadas. Es la vulnerabilidad más común en infraestructura cloud.
Headers de seguridad mínimos en Next.js
const securityHeaders = [
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "X-Frame-Options", value: "DENY" },
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
{ key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=()" },
{
key: "Content-Security-Policy",
value: "default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self';"
},
{ key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload" },
];
const nextConfig = {
async headers() {
return [{ source: "/(.*)", headers: securityHeaders }];
},
};Regla: en producción: desactivar stack traces, eliminar X-Powered-By, revisar permisos de buckets y bases de datos, headers de seguridad en todas las respuestas.
A03: Software Supply Chain Failures (NUEVA)
La categoría nueva más importante de 2025. El 50% de los encuestados de la comunidad OWASP la clasificaron como riesgo número 1. Expande la antigua A06 (Vulnerable and Outdated Components) al ecosistema completo: dependencias, pipelines CI/CD, registros de paquetes, build systems y distribución.
Casos reales que motivaron la categoría:
- SolarWinds (2020) — backdoor inyectado en el build system que afectó a 18.000 organizaciones incluyendo agencias federales de EEUU.
- Shai-Hulud (2025) — primer worm auto-propagable en npm. Infectó 500+ versiones de paquetes usando tokens npm exfiltrados para publicar versiones maliciosas automáticamente.
- Bybit (2025) — robo de 1.500 millones de dólares mediante compromiso de software de wallet que solo se activaba cuando se usaba el wallet objetivo.
Mitigaciones
# Auditoría de dependencias
npm audit --audit-level=critical
# SCA con Snyk
npx snyk test
# Lockfiles siempre en el repo (no en .gitignore)
git add package-lock.json
# Verificar integridad con SBOM
npx @cyclonedx/cyclonedx-npm --output-file sbom.json
# En CI: fallar si hay CVEs críticas
npm audit --audit-level=critical || exit 1Práctica: Dependabot o Renovate activos, lockfiles en repo, MFA en cuentas npm/registry, firma de commits, npm audit en CI, monitorización continua de dependencias con Socket o Snyk.
A04: Cryptographic Failures
Datos sensibles expuestos por criptografía débil o inexistente. Incluye: transmisión sin TLS, hashing con MD5/SHA1, claves hardcodeadas y almacenamiento en texto plano.
// ❌ MD5 — roto desde 2004
import crypto from "crypto";
const hash = crypto.createHash("md5").update(password).digest("hex");
// ✅ bcrypt con salt automático
import bcrypt from "bcrypt";
const hash = await bcrypt.hash(password, 12);
const valid = await bcrypt.compare(input, storedHash);Regla: contraseñas con bcrypt/scrypt/Argon2. TLS 1.2+ en tránsito. Secrets en variables de entorno, nunca en el repositorio. Para detectar secrets en código: GitLeaks o TruffleHog.
A05: Injection
Baja al puesto 5, pero sigue siendo crítica. Datos no sanitizados interpretados como código: SQL injection, XSS, command injection, template injection.
SQL Injection
// ❌ Concatenación directa
const query = `SELECT * FROM users WHERE email = '${email}'`;
// ✅ Prepared statements
const result = await db.raw("SELECT * FROM users WHERE email = ?", [email]);XSS
// ❌ HTML sin sanitizar
<div dangerouslySetInnerHTML={{ __html: userComment }} />
// ✅ React escapa por defecto
<div>{userComment}</div>
// Si necesitas HTML, sanitiza
import DOMPurify from "dompurify";
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userComment) }} />A06: Insecure Design
No es un bug de código, es un fallo de arquitectura. El sistema hace exactamente lo que el código dice, pero la lógica permite abusos: reset de contraseña que confirma si el email existe, flujo de compra con precio negativo, rate limiting ausente.
Mitigación: threat modeling antes de escribir código. Para cada feature: ¿qué pasa si un atacante usa esto de forma no prevista? Documentar abusos posibles y diseñar controles antes de implementar.
A07: Authentication Failures
Renombrada desde "Identification and Authentication Failures" en 2021. Contraseñas sin restricciones, sesiones que no expiran, tokens predecibles, credential stuffing sin rate limiting.
Controles mínimos
- Contraseñas: mínimo 8 caracteres, verificar contra Have I Been Pwned API.
- Rate limiting en login: 5-10 intentos/minuto por IP+cuenta.
- Sesiones: idle timeout + absolute timeout, regenerar ID tras login.
- MFA: obligatorio para admin, recomendado para todos en apps críticas.
- JWT: expiración corta (15-30 min), refresh tokens en httpOnly cookie.
A08: Software or Data Integrity Failures
Pipelines CI/CD sin verificación de integridad, actualizaciones sin firma, deserialización insegura, scripts externos sin Subresource Integrity (SRI).
<!-- ❌ Sin SRI — CDN comprometido = tu código comprometido -->
<script src="https://cdn.example.com/lib.js"></script>
<!-- ✅ Con SRI — el navegador verifica el hash antes de ejecutar -->
<script
src="https://cdn.example.com/lib.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K..."
crossorigin="anonymous"
></script>Práctica: firmar commits y artefactos, verificar checksums en CI/CD, lockfiles siempre en el repo, SRI en scripts externos.
A09: Security Logging and Alerting Failures
Renombrada para añadir "Alerting". Sin logs ni alertas, un ataque puede pasar desapercibido durante meses. El tiempo medio de detección de una brecha es de 197 días (IBM Cost of a Data Breach 2023).
Qué logear
- Intentos de login (éxitos y fallos) con timestamp, IP y user-agent.
- Cambios de permisos, roles o datos sensibles.
- Accesos denegados (403) y errores de autorización.
- Acciones administrativas.
- Errores de validación que puedan indicar inyección.
Nunca logear: contraseñas, tokens de sesión, números de tarjeta.
Alertar: configurar alertas automáticas (PagerDuty, OpsGenie, Slack) para picos de 403, login failures concentrados y cambios de configuración fuera de horario.
A10: Mishandling of Exceptional Conditions (NUEVA)
Categoría nueva en 2025 con 24 CWEs mapeados y 769.581 ocurrencias documentadas. Cubre: manejo incorrecto de errores, fail open (el sistema falla dejando acceso abierto en vez de cerrado), null pointer dereferences, race conditions, desbordamientos y estados inconsistentes provocados por condiciones no contempladas.
Ejemplo: fail open
// ❌ Fail open — si el servicio de auth falla, se permite el acceso
app.use(async (req, res, next) => {
try {
req.user = await authService.verify(req.headers.authorization);
} catch (err) {
// El catch no bloquea: si auth falla, el request pasa sin user
console.error("Auth failed:", err);
}
next();
});
// ✅ Fail closed — si auth falla, se deniega
app.use(async (req, res, next) => {
try {
req.user = await authService.verify(req.headers.authorization);
next();
} catch (err) {
res.status(401).json({ error: "Authentication failed" });
}
});Principio: fail closed siempre. Si un subsistema falla (autenticación, autorización, validación, pago), la operación se deniega, no se permite. Manejar excepciones lo más cerca posible de donde ocurren, no en un catch genérico de alto nivel.
Herramientas de detección
| Tipo | Herramienta | Qué detecta | Precio |
|---|---|---|---|
| SAST | Semgrep | Injection, SSRF, secrets, patrones inseguros | Gratis (open source) |
| SAST | CodeQL (GitHub) | Taint analysis, flujo de datos contaminados | Gratis en repos públicos |
| DAST | OWASP ZAP | XSS, injection, misconfig, CSRF | Gratis (open source) |
| DAST | Burp Suite | Suite completa de pentesting web | Community gratis / Pro ~$449/año |
| SCA | Snyk / Socket | CVEs en dependencias + supply chain risks | Gratis (limitado) / Pro desde $25/mes |
| Secrets | GitLeaks / TruffleHog | API keys, tokens y passwords en el repo | Gratis (open source) |
| Headers | securityheaders.com | Headers HTTP ausentes o mal configurados | Gratis |
Integración en CI/CD
name: Security checks
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: npm audit
run: npm audit --audit-level=critical
- name: Semgrep SAST
uses: semgrep/semgrep-action@v1
with:
config: >-
p/owasp-top-ten
p/javascript
p/typescript
- name: GitLeaks secrets scan
uses: gitleaks/gitleaks-action@v2Checklist OWASP Top 10 (2025)
- ☐ Autorización verificada en cada endpoint, incluyendo validación de destino en peticiones server-side (A01)
- ☐ Headers de seguridad, sin stack traces en prod, sin defaults inseguros (A02)
- ☐ Dependabot/Renovate activo, lockfiles, npm audit en CI, SBOM generado (A03)
- ☐ Contraseñas con bcrypt/Argon2, TLS 1.2+ en tránsito, secrets fuera del repo (A04)
- ☐ Consultas parametrizadas, output escapado, input validado (A05)
- ☐ Threat modeling documentado antes de features críticas (A06)
- ☐ Rate limiting en login, MFA en admin, sesiones con expiración (A07)
- ☐ SRI en scripts externos, commits firmados, verificación de integridad en CI (A08)
- ☐ Logging de auth failures y accesos denegados, alertas automáticas configuradas (A09)
- ☐ Fail closed en auth/authz/validación, excepciones manejadas donde ocurren (A10)
- ☐ SAST + DAST + SCA integrados en pipeline CI
Conclusión
La versión 2025 del OWASP Top 10 refleja un cambio de foco: la cadena de suministro de software es ahora un vector de ataque de primera categoría, y el manejo incorrecto de excepciones entra por primera vez como riesgo documentado. El resto de categorías se reordena según datos empíricos, no opinión. Cada una tiene contramedidas técnicas concretas y herramientas gratuitas. El coste de implementar estas protecciones durante el desarrollo es una fracción del coste de remediar una brecha.
Si quieres que auditemos tu aplicación contra OWASP Top 10, escríbenos a /contacto. Y para cumplimiento normativo complementario, lee la guía de Consent Mode v2 y la checklist WCAG 2.2.
Preguntas frecuentes
¿Qué es OWASP Top 10?
OWASP Top 10 es un documento de referencia publicado por la Open Worldwide Application Security Project que cataloga las 10 categorías de riesgo más críticas en aplicaciones web. La versión 2025 se basa en datos de más de 500.000 aplicaciones analizadas e incluye dos categorías nuevas: Software Supply Chain Failures y Mishandling of Exceptional Conditions.
¿Qué cambió de OWASP Top 10 2021 a 2025?
Dos categorías nuevas (A03: Software Supply Chain Failures y A10: Mishandling of Exceptional Conditions), SSRF absorbido dentro de Broken Access Control, Security Misconfiguration sube al puesto 2, y la antigua A06 de componentes vulnerables se expande a supply chain completa. Injection baja al puesto 5.
¿Es suficiente cumplir OWASP Top 10 para considerar una aplicación segura?
No. OWASP Top 10 cubre las vulnerabilidades más comunes, no todas. Es un mínimo, no un máximo. Una aplicación que pasa OWASP Top 10 puede tener vulnerabilidades de lógica de negocio, race conditions o fallos criptográficos específicos que el Top 10 no contempla.
¿Qué herramientas detectan vulnerabilidades del OWASP Top 10?
SAST (análisis estático): Semgrep, SonarQube, CodeQL. DAST (análisis dinámico): OWASP ZAP, Burp Suite, Nuclei. SCA (dependencias y supply chain): Snyk, npm audit, Dependabot, Socket. La combinación SAST + DAST + SCA + revisión manual es el estándar profesional.
¿Por qué supply chain es ahora la posición 3?
Ataques como SolarWinds (2020), el worm npm Shai-Hulud (2025) y el robo de Bybit de 1.500 millones de dólares a través de software de wallet comprometido (2025) demostraron que la cadena de suministro de software es un vector de ataque con impacto catastrófico. El 50% de los participantes en la encuesta comunitaria OWASP lo clasificaron como riesgo número 1.
¿Debo formar a mis desarrolladores en OWASP?
Sí. El 70-80% de las vulnerabilidades se introducen en fase de desarrollo. OWASP ofrece recursos gratuitos (WebGoat, Juice Shop) para práctica en entorno controlado. Formar al equipo es la inversión con mejor ROI en seguridad.
¿Tienes un proyecto en mente?
Cuéntanos qué necesitas y te proponemos la mejor solución sin compromiso.
Hablar con el equipo →