Command Palette

Search for a command to run...

ES·EN

Nivel 1 · 25 min

OWASP Top 10: La Base

El OWASP Top 10 es el documento de awareness estándar de la industria sobre riesgos de seguridad en aplicaciones web. La edición 2021 reordenó las prioridades en base a una década de datos de breaches: Broken Access Control saltó del puesto #5 al #1 porque se encontró en el 94% de las apps testeadas. Memorizar las categorías no es el objetivo — entender los modos de falla subyacentes sí lo es.

Las Categorías 2021 en Orden

A01 Broken Access Control (era #5) — IDOR, falta de autorización a nivel de función, force-browsing. A02 Cryptographic Failures (era Sensitive Data Exposure) — falta de TLS, cifrados débiles (RC4, MD5), claves hardcodeadas. A03 Injection (era #1) — SQLi, NoSQLi, comandos, LDAP. A04 Insecure Design — categoría nueva que cubre la falta de threat-modeling. A05 Security Misconfiguration — credenciales por defecto, errores verbosos, buckets S3 abiertos. A06 Vulnerable and Outdated Components — incidentes tipo log4shell. A07 Identification and Authentication Failures — credential stuffing, fijación de sesión. A08 Software and Data Integrity Failures — updates sin firmar, supply chain estilo npm event-stream. A09 Security Logging and Monitoring Failures. A10 Server-Side Request Forgery (SSRF) — el breach de Capital One de 2019.

Por Qué Broken Access Control Llegó al #1

El control de acceso responde a la pregunta ''¿este usuario puede ejecutar esta acción sobre este recurso?''. El reordenamiento de 2021 fue impulsado por datos: el 94% de las apps de testeo de OWASP tenían al menos una falla de access control, con un promedio de 3.81 ocurrencias por app. El ejemplo clásico es IDOR (Insecure Direct Object Reference): GET /api/invoices/12345 devuelve la factura 12345 sin chequear que el usuario que la pide sea el dueño. Un atacante itera el ID. La escalación vertical de privilegios es similar — un usuario regular llama a POST /admin/users y al endpoint le falta la anotación @PreAuthorize. La defensa es centralizar la autorización (un policy engine, no checks dispersos como if (user.isAdmin()) por 200 controllers), denegar por defecto, y escribir tests de access control que prueben todos los endpoints como usuario no autorizado.

Cryptographic Failures e Injection en 2026

A02 Cryptographic Failures cubre todo el espectro de ''el dato era confidencial, ya no lo es''. Ejemplos concretos: un backend que expone /metrics sobre HTTP plano dentro de una VPC porque ''la red es privada'' (no lo es — el movimiento lateral pasa), un servicio de autenticación que usa SHA-256 para hashear contraseñas (crackeable en segundos con hashcat en una sola GPU), cifrar backups de la base con AES-CBC sin chequeo de integridad (ataques padding oracle). A03 Injection sigue en el Top 10 porque el patrón inseguro (concatenación de strings en queries / shell / filtros LDAP) sigue apareciendo en código nuevo — aunque el fix (parameterized statements) se conoce hace 25 años.

Puntos clave

  • Broken Access Control es #1 en 2021 — encontrado en el 94% de las apps testeadas. Centralizá la autorización, denegá por defecto, testeá todos los endpoints como usuario no autorizado.
  • Cryptographic Failures es más amplio que ''falta de TLS'' — hashes débiles para passwords, falta de chequeos de integridad, claves hardcodeadas y backups sin cifrar también cuentan.
  • El Top 10 es awareness, no un checklist. Combinalo con threat modeling (STRIDE) para encontrar riesgos específicos de tu sistema.

Code example

// A01 Broken Access Control — IDOR vulnerable
@GetMapping("/api/invoices/{id}")
public Invoice get(@PathVariable Long id) {
  return invoiceRepo.findById(id).orElseThrow();
  // BUG: no chequea que el usuario actual sea dueño de esta factura
}

// CORREGIDO — chequeo de propietario + denegar por defecto
@GetMapping("/api/invoices/{id}")
@PreAuthorize("hasPermission(#id, 'Invoice', 'read')")
public Invoice get(@PathVariable Long id, Principal user) {
  Invoice inv = invoiceRepo.findById(id).orElseThrow();
  if (!inv.getOwnerId().equals(user.getName())) {
    throw new AccessDeniedException("no es tu factura");
  }
  return inv;
}