← Blog
·11 min read

OWASP A01 Control de Acceso Roto

El control de acceso roto es el riesgo #1 del OWASP Top 10. Esta guía explica IDOR, JWT tampering y rutas sin auth — con ejemplos reales en Next.js y cómo corregirlos.

Rod

Founder & Developer

El control de acceso roto es el riesgo #1 del OWASP Top 10 2021 — y no es por poco margen. OWASP lo encontró en el 34% de las aplicaciones evaluadas, subiendo desde el puesto #5 en 2017. Tiene 34 CWEs (Common Weakness Enumerations) mapeadas, más que cualquier otra categoría. Y es uno de los tipos de vulnerabilidad más fáciles de introducir sin darte cuenta — especialmente en apps JavaScript modernas donde la lógica de auth se dispersa entre middleware, componentes y rutas de API.

Esta guía cubre cómo se ve el control de acceso roto en el código que escribes todos los días, cómo ocurrieron filtraciones reales por esta razón, y qué hacer al respecto.


¿Qué es el Control de Acceso Roto?

La autenticación responde "¿quién eres?". La autorización responde "¿qué tienes permitido hacer?". El control de acceso roto es lo que pasa cuando la autenticación funciona bien, pero la autorización no.

El usuario inicia sesión. Tu app lo reconoce. Luego navega a /api/usuarios/456 — y tu app devuelve los datos del Usuario 456 aunque la persona conectada sea el Usuario 123. Nadie bypasseó la página de login. El token de auth era válido. Tu código simplemente nunca verificó si el usuario autenticado tenía permiso para ver ese recurso específico.

Ese hueco es el control de acceso roto. La entrada oficial A01:2021 de OWASP lo describe como la falla en hacer cumplir las restricciones sobre lo que los usuarios autenticados pueden hacer. La palabra clave es "hacer cumplir". Probablemente quisiste restringir el acceso. Pero la intención sin aplicación práctica es solo un comentario en tu código.

Según el reporte State of Software Security 2025 de Veracode, las fallas de control de acceso siguen siendo uno de los hallazgos más frecuentes en evaluaciones de aplicaciones web — y aparecen cada vez más en código generado por IA que optimiza para endpoints que funcionan, no para endpoints seguros.

La enciclopedia de control de acceso roto en Data Hogo cubre la taxonomía de la vulnerabilidad con más detalle. Este post se enfoca en los patrones que vas a reconocer en tu propio codebase.


Los Cinco Patrones que Te Meten en Problemas

1. IDOR — Referencia Directa a Objetos Insegura

Este es el más común. Tu endpoint toma un ID de la URL o del body y devuelve el registro correspondiente — sin verificar la propiedad.

// MAL: Obtiene los datos de cualquier usuario — sin verificación de propiedad
app.get('/api/ordenes/:ordenId', async (req, res) => {
  const orden = await db.query(
    'SELECT * FROM ordenes WHERE id = $1',
    [req.params.ordenId]
  );
  return res.json(orden);
});

El Usuario 123 está conectado. Cambia ordenId de 100 a 101 en la URL. Ahora ve la orden del Usuario 456 — incluyendo su dirección, referencia de método de pago e historial de compras.

El patrón IDOR está documentado como CWE-284 (Control de Acceso Inadecuado). El fix es directo: siempre incluye la condición de propiedad en tu query.

// BIEN: Verificación de propiedad integrada en la query
app.get('/api/ordenes/:ordenId', async (req, res) => {
  // req.user es poblado por el middleware de auth
  const orden = await db.query(
    'SELECT * FROM ordenes WHERE id = $1 AND user_id = $2',
    [req.params.ordenId, req.user.id]
  );
 
  if (!orden) {
    // Devuelve 404, no 403 — no confirmes que el recurso existe
    return res.status(404).json({ error: 'No encontrado' });
  }
 
  return res.json(orden);
});

Devolver 404 en lugar de 403 es intencional. Devolver 403 confirma que el recurso existe — información que no quieres darle a alguien que está probando tu API.

2. Falta de Control de Acceso a Nivel de Función

Algunas rutas solo deberían poder llamarlas los admins. Si no verificas ese rol explícitamente, cualquier usuario autenticado puede llamarlas.

// MAL: Endpoint de admin con solo autenticación — sin verificación de rol
export async function DELETE(req: Request) {
  const { userId } = await supabase.auth.getUser();
  if (!userId) return Response.json({ error: 'No autorizado' }, { status: 401 });
 
  // Elimina cualquier usuario — sin verificar que quien llama es admin
  await db.eliminarUsuario(req.body.targetUserId);
  return Response.json({ success: true });
}

Este endpoint verifica que quien llama esté conectado. No verifica que sea admin. Cualquier usuario autenticado puede eliminar cualquier cuenta.

// BIEN: Autenticación + verificación de rol antes de operación destructiva
export async function DELETE(req: Request) {
  const { data: { user } } = await supabase.auth.getUser();
  if (!user) return Response.json({ error: 'No autorizado' }, { status: 401 });
 
  // Obtiene el perfil del solicitante para verificar su rol
  const { data: perfil } = await supabase
    .from('profiles')
    .select('role')
    .eq('id', user.id)
    .single();
 
  if (perfil?.role !== 'admin') {
    return Response.json({ error: 'Prohibido' }, { status: 403 });
  }
 
  await db.eliminarUsuario(req.body.targetUserId);
  return Response.json({ success: true });
}

La verificación del rol ocurre del lado del servidor, contra tu base de datos — no desde un campo en el body de la petición ni desde un claim del JWT que no has verificado.

Los endpoints sin autenticación son uno de los hallazgos más frecuentes cuando escaneamos repositorios reales en Data Hogo. Aparece tanto como falta de middleware de auth como falta de verificación de roles en operaciones sensibles.

3. Lógica de Auth Solo en el Frontend

Este es común en apps React y Next.js. El developer agrega una verificación en la UI — "si el usuario no es admin, no mostrar el botón de eliminar". La ruta de API no tiene ninguna verificación.

// MAL: El botón está oculto en la UI, pero la API no tiene ningún check
// El frontend oculta el botón — pero cualquiera puede llamar este endpoint directamente
export async function POST(req: Request) {
  const { targetUserId } = await req.json();
  await db.banearUsuario(targetUserId);
  return Response.json({ success: true });
}

La verificación en el frontend no vale nada como control de seguridad. Cualquiera con las dev tools del navegador o curl puede llamar tu API directamente. La lógica de auth solo en el frontend es un patrón de falla específico — y es uno de los checks que Data Hogo ejecuta en cada escaneo de repos Next.js.

Cada decisión de control de acceso debe hacerse cumplir del lado del servidor. El frontend puede ocultar elementos para la UX. No puede prevenir llamadas a la API.

4. JWT Tampering y Escalada de Privilegios

Los JWTs (JSON Web Tokens) están firmados, no cifrados. El payload es legible por cualquier persona que tenga acceso al token. Si tu app confía en los claims del payload del JWT sin verificarlos del lado del servidor, tienes un problema.

// MAL: Confiar en el claim de rol del payload del JWT sin verificación
const decoded = jwt.decode(token); // decode, ¡no verify!
if (decoded.role === 'admin') {
  // Un atacante puede falsificar este claim si la clave de firma es débil o está expuesta
  return datosAdmin;
}

El approach correcto es verificar la firma primero, y luego consultar el rol en tu base de datos — no desde el payload del token.

// BIEN: Verifica la firma, luego obtiene el rol de la base de datos
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const usuario = await db.getUsuarioPorId(decoded.sub);
 
// Confía en la base de datos, no en el claim del token
if (usuario.role !== 'admin') {
  return Response.json({ error: 'Prohibido' }, { status: 403 });
}

La escalada de privilegios vía manipulación de JWT es un hallazgo frecuente en apps que usan implementaciones de auth custom en lugar de una librería bien probada.

5. Path Traversal

El path traversal le permite a un atacante leer archivos fuera del directorio que tenías intención de servir, inyectando secuencias ../ en un parámetro de ruta de archivo.

// MAL: Servir archivos desde una ruta enviada por el usuario sin sanitización
app.get('/archivos/:nombre', (req, res) => {
  const rutaArchivo = path.join('/var/app/uploads', req.params.nombre);
  res.sendFile(rutaArchivo);
});

Una petición a /archivos/../../etc/passwd resuelve a /etc/passwd. El patrón de path traversal (CWE-22) puede exponer archivos de configuración del servidor, archivos de variables de entorno y claves privadas.

// BIEN: Resuelve la ruta y verifica que siga dentro del directorio de uploads
app.get('/archivos/:nombre', (req, res) => {
  const dirUploads = path.resolve('/var/app/uploads');
  const rutaArchivo = path.resolve(dirUploads, req.params.nombre);
 
  // Asegura que la ruta resuelta empiece con el directorio de uploads
  if (!rutaArchivo.startsWith(dirUploads + path.sep)) {
    return res.status(403).json({ error: 'Prohibido' });
  }
 
  res.sendFile(rutaArchivo);
});

Cómo Se Ve una Filtración Real

Optus 2022 — API sin Auth Expone 9.8 Millones de Registros

En septiembre de 2022, la empresa australiana de telecomunicaciones Optus sufrió una filtración que expuso los datos personales de 9.8 millones de clientes — incluyendo nombres, fechas de nacimiento, números de teléfono, correos electrónicos y números de documentos de identidad.

La causa raíz: un endpoint de API accesible desde internet público que no requería autenticación. Era un sistema legado que había sido expuesto sin requerir ningún token. Un atacante lo descubrió, iteró secuencialmente a través de IDs de clientes y extrajo millones de registros.

Esto es control de acceso roto en su forma más básica — específicamente, un check de autenticación faltante en un endpoint que devolvía datos sensibles. Sin exploit sofisticado. Sin zero-day. Solo GET /api/clientes/1, luego GET /api/clientes/2, repetido 9.8 millones de veces.

La filtración resultó en una multa de 1.36 millones de dólares australianos. El fix fue trivialmente simple: requerir un token de autenticación.

Capital One 2019 — SSRF Combinado con Roles IAM Sobreper misados

La filtración de Capital One expuso datos de aproximadamente 106 millones de clientes. El mecanismo fue una vulnerabilidad SSRF (Server-Side Request Forgery) en la configuración de un firewall de aplicaciones web, que permitió a un atacante consultar el servicio de metadata de instancias EC2 de AWS en 169.254.169.254. Ese servicio devolvió credenciales temporales de IAM.

La falla de control de acceso: esas credenciales IAM tenían permisos excesivos. La instancia EC2 tenía un rol IAM con acceso a S3 mucho más amplio del que necesitaba. Un atacante que obtuviera las credenciales podía listar y descargar archivos de buckets de S3 que nunca debería haber podido tocar.

Esto es control de acceso roto a nivel de infraestructura — un proceso de servidor con permisos que no necesitaba, combinado con una vulnerabilidad que le permitió a un atacante impersonar ese proceso. El principio de mínimo privilegio es una forma de control de acceso, y aplica a tus recursos en la nube tanto como a tus rutas de API.


Checklist de Prevención de Control de Acceso Roto

No son recomendaciones abstractas. Son las cosas específicas a implementar.

Denegación por defecto. Todo recurso debe ser inaccesible a menos que tu código explícitamente otorgue acceso. No escribas código que permite acceso y luego agrega restricciones. Escribe código que deniega acceso y luego agrega permisos.

Aplicación del lado del servidor, siempre. Cada decisión de control de acceso ocurre en el servidor. Los checks del frontend son UX, no seguridad.

Usa middleware para autenticación, usa handlers para autorización. Tu middleware de auth verifica la sesión. Cada handler verifica que el usuario autenticado tiene permiso para realizar la operación específica sobre el recurso específico.

Agrega la propiedad a tus queries. No obtengas un registro por ID y luego verifiques la propiedad en el código de la app. Incluye la condición de propiedad en la query de base de datos misma — así un registro sin propiedad devuelve cero filas en lugar de una fila que tu código tiene que decidir si mostrar.

Implementa RBAC correctamente. Los roles pertenecen a tu base de datos. No guardes roles en JWTs y los confíes tal cual. Consulta el rol fresco para operaciones sensibles.

Prueba los controles de acceso — explícitamente. Para cada endpoint protegido, escribe un test que inicia sesión como Usuario A e intenta acceder al recurso del Usuario B. Debería devolver 404. Si devuelve 200, tienes un IDOR.

Registra las fallas de control de acceso. Un pico en respuestas 403 contra un patrón de IDs secuenciales es un ataque de IDOR activo. Quieres saber cuando esto pasa.

Audita los open redirects. Un open redirect puede usarse para bypassear controles de acceso basados en el referrer o para phishing con un dominio de confianza como punto de entrada.


Cómo Data Hogo Detecta Control de Acceso Roto

Cuando escaneamos repositorios en Data Hogo, los hallazgos de control de acceso roto caen en varios patrones detectables:

  • Endpoints sin middleware de auth — rutas que nunca llaman tu función de verificación de auth
  • Lógica de auth solo en el frontend — renderizado condicional o route guards sin un check correspondiente del lado del servidor
  • Referencias directas a objetos sin joins de propiedad — queries de base de datos que usan IDs enviados por el usuario sin una condición WHERE user_id = $usuario_actual
  • CORS mal configurado — headers Access-Control-Allow-Origin demasiado permisivos que habilitan peticiones cross-origin desde dominios no confiables
  • Rutas de admin sin verificación de rol — endpoints en /admin/ o con operaciones destructivas que solo verifican autenticación

Escaneamos más de 50 repositorios mientras construíamos la librería de patrones de Data Hogo. Los checks de autenticación faltantes fueron el hallazgo crítico más común. Los patrones IDOR — identificables en queries de base de datos que carecen de condiciones de propiedad — fueron el segundo.

Escanea tu repo gratis para ver una lista priorizada de hallazgos de control de acceso en tu codebase. El primer escaneo es gratis, sin tarjeta de crédito.


Preguntas Frecuentes

¿Qué es el control de acceso roto en aplicaciones web?

El control de acceso roto ocurre cuando tu app no verifica correctamente qué puede hacer un usuario autenticado. Un usuario puede ver datos de otro usuario, llamar un endpoint de admin, o modificar registros que no le pertenecen — no porque haya bypasseado el login, sino porque tu código nunca verificó si tenía permiso para hacer esa acción específica. Es el riesgo #1 del OWASP Top 10 2021, encontrado en el 34% de las aplicaciones evaluadas.

¿Qué es una vulnerabilidad IDOR?

IDOR (Insecure Direct Object Reference) es la forma más común de control de acceso roto. Ocurre cuando un endpoint usa un identificador enviado por el usuario — como /api/ordenes/123 — para obtener un recurso, pero nunca verifica si el usuario que hace la petición es el dueño real de ese recurso. Cambiar 123 por 124 en la URL no debería exponer la orden de otro usuario, pero si no hay verificación de propiedad, sí lo hace.

¿Cómo corrijo el control de acceso roto en una ruta de API de Next.js?

Siempre verifica la autenticación primero, luego verifica la autorización. Comprueba que el usuario autenticado realmente sea el dueño del recurso que está pidiendo — no confíes en el ID que viene en la URL o en el body. Usa middleware para la autenticación y verificaciones explícitas de propiedad dentro de cada handler. Nunca relies en que un recurso sea "difícil de encontrar" como control de seguridad.

¿Qué filtraciones reales fueron causadas por control de acceso roto?

Varias filtraciones importantes tienen este origen. La brecha de Optus en 2022 expuso los datos de 9.8 millones de clientes porque un endpoint de API devolvía registros de clientes sin requerir ningún token de autenticación. La filtración de Capital One en 2019 combinó SSRF con roles IAM con permisos excesivos. MOVEit en 2023 tuvo una inyección SQL que permitió bypassear completamente el control de acceso a datos de miles de organizaciones.

¿El escaneo automático detecta control de acceso roto?

Parcialmente. Las herramientas de análisis estático como Data Hogo pueden detectar rutas sin middleware de autenticación, endpoints sin verificación de auth, y lógica de autenticación solo en el frontend. Lo que no pueden verificar automáticamente es tu lógica de negocio — si el Usuario A está correctamente impedido de acceder a los datos del Usuario B depende de las reglas de tu app. El escaneo automático detecta los patrones obvios; la revisión manual detecta los casos límite.

OWASPcontrol de accesoIDORautorizaciónNext.jsseguridadguías