← Blog
·12 min read

OWASP Top 10 explicado con código: guía completa 2026

OWASP Top 10 explicado en español con ejemplos de código reales para cada categoría. Vulnerabilidades web más comunes y cómo detectarlas en tu app.

Rod

Founder & Developer

El OWASP Top 10 es la lista de referencia de las vulnerabilidades web más peligrosas. La publica OWASP — Open Web Application Security Project — y se basa en datos reales de miles de aplicaciones analizadas. Si solo vas a aprender 10 cosas de seguridad este año, que sean estas.

Esta guía no es un resumen de Wikipedia. Cada categoría tiene código vulnerable, código seguro, y lo que deberías buscar en tu propio proyecto.


A01: Broken Access Control — Control de Acceso Roto

El más común. Ocurre cuando tu app no verifica correctamente si alguien tiene permiso para hacer lo que está intentando hacer.

El ejemplo clásico: cambiar el ID en la URL y acceder a datos de otro usuario.

// MAL: asume que el usuario solo va a pedir sus propios datos
app.get("/api/orders/:orderId", async (req, res) => {
  const order = await db.orders.findById(req.params.orderId);
  return res.json(order); // cualquiera puede pedir cualquier orden
});
 
// BIEN: verifica que la orden pertenece al usuario autenticado
app.get("/api/orders/:orderId", authenticate, async (req, res) => {
  const order = await db.orders.findOne({
    id: req.params.orderId,
    userId: req.user.id, // solo sus propias órdenes
  });
  if (!order) return res.status(404).json({ error: "No encontrado" });
  return res.json(order);
});

Tenemos una guía completa de Broken Access Control con más patrones y casos edge.


A02: Cryptographic Failures — Fallas Criptográficas

Datos sensibles mal protegidos. Contraseñas en texto plano, comunicación sin HTTPS, algoritmos de hash débiles.

// MAL: MD5 es reversible con rainbow tables
const passwordHash = crypto.createHash("md5").update(password).digest("hex");
 
// BIEN: bcrypt con salt rounds adecuados
import bcrypt from "bcrypt";
const passwordHash = await bcrypt.hash(password, 12);

Si usas Supabase o cualquier base de datos, revisa que ningún campo sensible se esté guardando en texto plano. La guía de fallas criptográficas cubre HTTPS, cifrado en reposo, y gestión de claves.


A03: Injection — Inyección

SQL injection, command injection, LDAP injection. El código construye una consulta o comando usando input del usuario sin sanitizarlo.

// MAL: SQL injection — un usuario puede destruir tu base de datos
const query = `SELECT * FROM users WHERE email = '${req.body.email}'`;
// Si email = "' OR '1'='1", devuelve todos los usuarios
 
// BIEN: consultas parametrizadas
const query = "SELECT * FROM users WHERE email = $1";
const result = await db.query(query, [req.body.email]);
// MAL: command injection
const { filename } = req.body;
exec(`convert ${filename} output.pdf`); // filename podría ser "file; rm -rf /"
 
// BIEN: valida y sanitiza antes de usar en comandos
const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "");
exec(`convert ${safeName} output.pdf`);

A04: Insecure Design — Diseño Inseguro

No es un bug de implementación — es que la arquitectura misma no considera la seguridad. Flujos que nunca debieron existir.

Ejemplo: un sistema de recuperación de contraseña que envía la contraseña actual por email en lugar de un link para cambiarla. No hay forma de "parcharlo" — hay que rediseñarlo.

Otro patrón común: guardar tokens de sesión en localStorage en lugar de cookies HttpOnly.

// MAL: el token en localStorage es accesible desde JavaScript
localStorage.setItem("authToken", token); // XSS puede robarlo
 
// BIEN: cookie HttpOnly — JavaScript no puede leerla
res.cookie("session", token, {
  httpOnly: true,  // no accesible desde JS
  secure: true,    // solo HTTPS
  sameSite: "lax", // protección CSRF
});

A05: Security Misconfiguration — Configuración Insegura

La categoría más amplia. Headers de seguridad faltantes, CORS mal configurado, mensajes de error que exponen información interna, servicios de cloud con permisos por defecto.

// MAL: el error le dice al atacante exactamente qué falló
app.use((err, req, res, next) => {
  res.status(500).json({
    error: err.message,
    stack: err.stack,
    query: err.query, // nunca mandes la query SQL al cliente
  });
});
 
// BIEN: mensaje genérico para afuera, detalle para tus logs
app.use((err, req, res, next) => {
  console.error({ err, userId: req.user?.id, path: req.path });
  res.status(500).json({ error: "Error interno del servidor" });
});

Puedes revisar los security headers de tu URL ahora mismo — muchas configuraciones inseguras son visibles sin ni siquiera acceder al código.


A06: Vulnerable and Outdated Components — Componentes Vulnerables

Tus dependencias tienen bugs. Algunas tienen CVEs conocidos con exploits públicos. Si no las actualizas, alguien puede usarlos contra ti.

# Revisa tus dependencias con vulnerabilidades conocidas
npm audit
 
# Actualiza las que puedes actualizar sin romper nada
npm audit fix
 
# Para vulnerabilidades que requieren cambios mayores
npm audit fix --force  # cuidado: puede romper cosas

El problema no es solo npm audit — es que muchos CVEs no aparecen ahí. Usamos la base de datos OSV (Open Source Vulnerabilities) de Google en paralelo para mayor cobertura.


A07: Identification and Authentication Failures — Fallas de Autenticación

Contraseñas débiles permitidas, sin límite de intentos, tokens de sesión predecibles, sin verificación en 2FA.

// MAL: cualquier contraseña pasa
if (password.length >= 1) {
  createUser(email, password);
}
 
// BIEN: validación mínima razonable
const passwordSchema = z.string()
  .min(8, "Mínimo 8 caracteres")
  .regex(/[A-Z]/, "Al menos una mayúscula")
  .regex(/[0-9]/, "Al menos un número");
 
// Y límite de intentos de login
const loginAttempts = await redis.incr(`login:${ip}`);
if (loginAttempts > 10) {
  return res.status(429).json({ error: "Demasiados intentos. Espera 15 minutos." });
}

A08: Software and Data Integrity Failures — Fallas de Integridad

Tu pipeline de CI/CD descarga dependencias sin verificar su integridad. Alguien compromete un paquete npm y todos los proyectos que lo usan están comprometidos.

// package-lock.json o yarn.lock — siempre commitéalos
// Fijan las versiones exactas y los checksums
{
  "node_modules/some-package": {
    "version": "1.2.3",
    "resolved": "https://registry.npmjs.org/some-package/-/some-package-1.2.3.tgz",
    "integrity": "sha512-xxxxx" // verifica que el paquete no fue modificado
  }
}

Nunca ignores el lockfile. Nunca hagas npm install --no-package-lock en producción.


A09: Security Logging and Monitoring Failures — Fallas de Registro

Tu app no registra eventos importantes. Cuando algo malo pasa, no tienes forma de saber qué pasó, cuándo, ni quién lo causó.

// MAL: silencioso cuando falla la autenticación
async function login(email, password) {
  const user = await db.users.findByEmail(email);
  if (!user || !await bcrypt.compare(password, user.password)) {
    return null; // nadie sabe que esto falló
  }
  return user;
}
 
// BIEN: registra los eventos de seguridad
async function login(email, password, ip) {
  const user = await db.users.findByEmail(email);
  if (!user || !await bcrypt.compare(password, user.password)) {
    logger.warn({ event: "login_failed", email, ip, timestamp: new Date() });
    return null;
  }
  logger.info({ event: "login_success", userId: user.id, ip });
  return user;
}

A10: Server-Side Request Forgery (SSRF)

Tu servidor hace un request HTTP a una URL que viene del usuario. El atacante te manda una URL interna (http://169.254.169.254/metadata en AWS) y tu servidor la fetcha con sus propios permisos.

// MAL: cualquier URL, incluyendo servicios internos
app.post("/api/preview", async (req, res) => {
  const { url } = req.body;
  const response = await fetch(url); // un atacante puede poner la IP del metadata de AWS
  return res.json({ content: await response.text() });
});
 
// BIEN: valida que la URL sea externa y en un dominio permitido
import { URL } from "url";
app.post("/api/preview", async (req, res) => {
  const parsed = new URL(req.body.url);
  const blocked = ["localhost", "127.0.0.1", "169.254.169.254", "::1"];
  if (blocked.includes(parsed.hostname)) {
    return res.status(400).json({ error: "URL no permitida" });
  }
  const response = await fetch(parsed.toString());
  return res.json({ content: await response.text() });
});

Cómo revisar tu app contra el OWASP Top 10

Leer la lista es un buen inicio. Revisarla en tu código es el siguiente paso.

Puedes hacer el análisis manual — toma horas por categoría, requiere saber exactamente qué buscar. O puedes conectar tu repo a un scanner que tiene reglas para cada una de estas categorías.

Data Hogo cubre las categorías principales del OWASP Top 10 con más de 250 reglas de detección. El scan toma menos de 5 minutos.

Escanea tu repo gratis →


Preguntas frecuentes

¿Qué es el OWASP Top 10?

El OWASP Top 10 es una lista de las 10 categorías de vulnerabilidades de seguridad más críticas en aplicaciones web, publicada por la Open Web Application Security Project. Se actualiza cada pocos años con datos reales de la industria y sirve como referencia estándar para developers y equipos de seguridad.

¿Cuál es la vulnerabilidad más común según OWASP?

Broken Access Control (Control de Acceso Roto) es el #1 del OWASP Top 10 desde 2021. Ocurre cuando una aplicación no verifica correctamente si un usuario tiene permiso para realizar una acción o acceder a un recurso específico.

¿Cómo puedo revisar si mi app tiene vulnerabilidades del OWASP Top 10?

La forma más rápida es conectar tu repo de GitHub a un scanner automatizado. Data Hogo cubre las categorías principales del OWASP Top 10 con más de 250 reglas de detección. El scan toma menos de 5 minutos y muestra cada hallazgo con explicación en español.

¿El OWASP Top 10 aplica solo a apps web?

Originalmente era solo para web, pero los principios aplican a APIs, apps móviles y microservicios. OWASP también publica listas específicas para APIs (OWASP API Security Top 10) y móviles (OWASP Mobile Top 10).

¿Con qué frecuencia se actualiza el OWASP Top 10?

OWASP actualiza la lista cada 3-4 años basándose en datos reales de vulnerabilidades reportadas. La versión actual es de 2021. La siguiente actualización se espera para 2025-2026.

owaspseguridad webvulnerabilidadestop 10código seguroguía