Atacó su propia API en AWS 4 veces: esto encontró

En pocas palabras: El desarrollador Yvan SAF desplegó dos versiones de una API Todo en AWS (API Gateway, Lambda, DynamoDB) y atacó la versión sin controles: encontró enumeración sin credenciales, XSS almacenado, IDOR y abuso de costo por falta de rate limiting. Publicó código, Terraform y capturas en GitHub, mostrando que cada falla se corrige con cambios puntuales de código, no con nueva infraestructura.

Un desarrollador atacó su propia API de Todo en AWS cuatro veces seguidas: enumeración sin credenciales, XSS almacenado, IDOR y abuso de costo por falta de rate limiting. Documentó cada ataque con capturas reales, publicó el código y Terraform completos, y mostró cómo cada falla se corrige con cambios de código puntuales, no con infraestructura nueva.

Las vulnerabilidades API AWS más comunes en aplicaciones serverless (API Gateway, Lambda, DynamoDB) surgen del lado que le corresponde al desarrollador dentro del Shared Responsibility Model de AWS: autenticación, permisos IAM y límites de tasa. AWS asegura la infraestructura física y el backbone de red; todo lo que corre arriba, incluida la lógica de tu API, es responsabilidad de quien la despliega.

En 30 segundos

  • Un desarrollador desplegó dos versiones idénticas de una API Todo en AWS (Client → API Gateway → Lambda → DynamoDB), una sin controles y otra hardened, según el artículo original en Dev.to.
  • Encontró cuatro fallas reales: enumeración total de datos, XSS almacenado, IDOR y abuso de costo con 5000 requests sin límite.
  • Cada fix fue mínimo: un Lambda Authorizer, un regex de validación, una comparación de userId y throttling en API Gateway.
  • El propio autor se equivocó tres veces durante las pruebas, incluida la exposición accidental de un JWT signing secret en una captura de terminal.
  • Todo el código, Terraform y scripts de ataque están públicos en el repositorio de GitHub.

¿Qué experimento hizo el desarrollador para probar la seguridad de una API propia en AWS?

Desplegó la misma API dos veces en la misma cuenta de AWS: una sin ningún control de seguridad y otra “hardened”, construida como la haría para un cliente real. Después atacó ambas desde su propia terminal y documentó qué se rompía y qué resistía.

El disparador fue escuchar demasiadas veces en entrevistas y foros de estudiantes la frase “estamos en la cloud, entonces estamos seguros”. El problema con esa frase es que confunde la seguridad de la infraestructura de AWS con la seguridad de lo que vos construís arriba. El Shared Responsibility Model de AWS traza una línea clara: AWS se encarga del hardware, del hypervisor, de la red física. Todo lo que está de tu lado (autenticación, permisos IAM, rate limiting) corre por tu cuenta.

Ambas versiones comparten la misma arquitectura básica: Client → API Gateway → Lambda → DynamoDB. Lo que cambia no son las cajas sino qué hay entre ellas y qué puede hacer cada componente. Las pruebas se hicieron sobre infraestructura propia, en línea con la política de pentesting de AWS, que permite testear recursos que uno mismo controla sin autorización previa. El código completo, los archivos Terraform y los scripts de ataque están en el repositorio público del proyecto.

¿Qué son las vulnerabilidades API AWS más comunes que encontró en la versión sin seguridad?

vulnerabilidades api aws diagrama explicativo

Encontró cuatro fallas concretas en la versión vulnerable: enumeración de datos sin autenticación, XSS almacenado, IDOR (Insecure Direct Object Reference) y abuso de costo por falta de rate limiting. Las cuatro son de las vulnerabilidades API AWS más frecuentes en aplicaciones serverless mal configuradas, y ninguna requirió herramientas sofisticadas para explotarse. Complementá con armar una arquitectura AWS sólida en producción.

Ponele que le pedís a tu API “dame mis tareas”. Si el handler no chequea quién sos, te puede terminar dando las tareas de todo el mundo. Eso es exactamente lo que pasó acá.

Enumeración: leer todo sin credenciales

La función “listar tareas” de la versión vulnerable ejecutaba un table.scan() sin filtrar por usuario, porque la ruta no tenía forma de saber quién llamaba. El desarrollador creó tareas para dos usuarios ficticios y llamó al endpoint de forma anónima: una sola request devolvió el dataset completo. Nada de esto requirió skill técnico particular, es lo que pasa por default cuando nadie le puso una cerradura a la puerta.

XSS almacenado: un script guardado tal cual

La versión vulnerable escribía en DynamoDB cualquier texto recibido, sin sanitizar. El autor envió <script>alert(document.cookie)</script> como título de una tarea y quedó guardado intacto en la base. Este proyecto es una demostración sin frontend implementado: no hay interfaz que renderice ese HTML, así que el payload no llega a ejecutarse en este experimento puntual. Pero el problema no depende de que exista o no un frontend: está en una API que nunca debió aceptar ese input sin sanitizar, porque cualquier cliente que sí renderice HTML (presente o futuro) queda expuesto.

IDOR: borrar la tarea de otro con un ID adivinado

La función de borrado de la versión vulnerable era esta, completa: delete_task(task_id) ejecutaba table.delete_item() sin ningún chequeo de propiedad. El autor creó una tarea a nombre de una víctima ficticia y, actuando como atacante sin relación alguna, la leyó y la borró. Ambas acciones devolvieron un 200 limpio. Y como el ataque de enumeración ya había regalado todos los IDs de la tabla, en la práctica los dos problemas encadenan: una request para juntar IDs, otra para actuar sobre cualquiera de ellos. Tema relacionado: los checks de seguridad que debería tener tu pipeline.

Abuso de costo: 5000 requests sin freno

El API Gateway vulnerable no tenía throttling configurado en ningún lado. El autor disparó 5000 requests seguidas contra el endpoint. Algunas fallaron, pero no porque algo protegiera la API: el límite de concurrencia default de Lambda de su propia cuenta se saturó momentáneamente, un accidente de configuración de cuenta, no una defensa diseñada. Cada una de esas 5000 invocaciones se factura. Una API sin rate limit no es solo un problema de exposición de datos: es una forma de que otro decida cuánto va a ser tu factura de AWS ese mes.

¿Cómo se corrige cada vulnerabilidad y cómo hacer pentesting ético a mi propia API?

Cada falla se corrigió con un cambio de código puntual, no con una reescritura de arquitectura. Un Lambda Authorizer resolvió la enumeración, un validador con regex resolvió el XSS, una comparación de userId resolvió el IDOR y el throttling nativo de API Gateway resolvió el abuso de costo.

  • Enumeración → 401 antes de tocar la tabla: un Lambda Authorizer valida un token firmado antes de que la request llegue al handler. Con un token válido, la consulta se filtra por un índice de DynamoDB específico del usuario, así que ni siquiera un llamado legítimo devuelve datos ajenos.
  • XSS → 400 con mensaje claro: cada campo pasa por un validador con el patrón DANGEROUS_PATTERN (una regex que detecta tags HTML, javascript: y atributos on*=) antes de llegar a la base. Si detecta algo sospechoso, rechaza con 400 y no escribe nada.
  • IDOR → 403 con una sola comparación: el fix fue agregar if item.get("userId") != user_id: return response(403, ...) justo antes del borrado. No hacía falta un sistema de control de acceso elaborado, solo faltaba hacer esa pregunta.
  • Abuso de costo → 429 en el umbral elegido: con throttling configurado directamente en API Gateway, pasado cierto número de requests por segundo, el cliente recibe 429 Too Many Requests en vez de saturar Lambda o inflar la factura.

La versión hardened también suma trazabilidad: cada request lleva un trace de X-Ray desde API Gateway hasta DynamoDB, y cada línea de log incluye el trace ID que lo vincula a esa request exacta. Bloquear el ataque es la mitad del trabajo. Poder reconstruir después, con precisión, qué pasó y sobre qué recurso, es la otra mitad, y suele ser la parte que se salta en las demos de seguridad.

Si querés replicar este tipo de pentesting ético sobre tu propia infraestructura, el criterio práctico es simple: probá primero contra un entorno sandbox que puedas destruir sin costo, documentá cada request con su respuesta exacta (código HTTP, payload, timestamp) y compará siempre versión insegura contra versión corregida antes de sacar conclusiones. Sin ese control A/B, es difícil saber si un fix realmente cerró la falla o si solo cambió el mensaje de error.

¿Qué errores cometió el propio autor al probar esto?

El autor documentó tres tropiezos metodológicos propios en docs/lessons-learned.md, y son tan útiles como los cuatro ataques. Ninguno es una falla de AWS: son errores de configuración y de proceso que cualquiera que haga este tipo de pruebas puede repetir. Te puede servir nuestra cobertura de qué hacer si una clave AWS se ve comprometida.

El primero: durante la prueba de rate limiting, todas las requests devolvían 403 Forbidden en vez del 429 esperado. Su primera hipótesis fue un bug en el throttling. En realidad, su token de prueba había expirado. En las HTTP APIs de API Gateway, un Lambda Authorizer que rechaza activamente una request devuelve 403 por la respuesta default, mientras que una request sin token devuelve 401. Son dos causas distintas con un rechazo parecido, y el código de estado solo no te dice cuál es cuál.

El segundo: configuró el throttling en 100 requests por segundo, generó tráfico suficiente para dispararlo y recibió una pared de errores 503 en vez de 429. El límite de concurrencia default de Lambda de su cuenta personal era más bajo que el throttle configurado, así que el tráfico chocó contra ese techo antes de acumular volumen suficiente para llegar al límite que realmente quería probar. Terminó bajando el throttle para la prueba y anotando por qué, porque alguien con límites de cuenta distintos se va a topar con una pared diferente.

El tercero, el más incómodo: en algún momento, mientras capturaba una pantalla de terminal, su JWT signing secret quedó expuesto en texto plano en un comando de shell exportado. Un recordatorio simple de que la higiene operativa (limpiar variables de entorno antes de sacar capturas) es tan parte de la seguridad como el código mismo.

¿Qué consecuencia práctica tiene esto para quienes desarrollan APIs en AWS?

La lectura práctica es directa: la seguridad del lado del cliente en AWS (autenticación, permisos IAM, rate limiting) es responsabilidad exclusiva de quien despliega la API, no de AWS. Los cuatro fixes de este experimento fueron cambios de código chicos, no infraestructura nueva ni servicios adicionales contratados. Lo explicamos a fondo en implementar permisos IAM correctamente en AWS.

Eso cambia la conversación en un equipo. No hace falta un rediseño de arquitectura para cerrar estas cuatro fallas: hace falta que alguien se siente a preguntar “¿quién puede llamar a este endpoint?” y “¿qué pasa si mando 5000 requests seguidas?” antes de que la API llegue a producción. El costo de no hacerlo no es solo una brecha de datos, también es una factura de AWS inflada por tráfico que nadie autorizó.

Errores comunes al asegurar una API serverless en AWS

  • Confundir “está en la nube” con “está protegido”: AWS asegura el hardware y la red, no tu lógica de autenticación ni tus permisos IAM. Esa línea la traza el Shared Responsibility Model, y cruzarla sin saberlo es el error de fondo detrás de las otras cuatro fallas.
  • Confiar en el código de estado HTTP sin verificar la causa real: como mostró el propio autor, un 403 puede ser un token expirado o un rechazo real del authorizer. Antes de dar por resuelto un bug, revisá el log completo con el trace ID, no solo el código de respuesta.
  • Asumir que el límite de concurrencia de Lambda es una defensa: es un techo de cuenta, no una protección diseñada. Configurar throttling explícito en API Gateway es la única forma de controlar el umbral real de tu API.
  • No filtrar consultas por usuario en DynamoDB: un scan() sin índice por usuario devuelve todo lo que hay en la tabla a cualquiera que pregunte. La corrección es tan simple como consultar por un índice específico del userId autenticado.

Preguntas Frecuentes

¿Qué es un ataque IDOR y cómo afecta a las APIs en AWS?

IDOR (Insecure Direct Object Reference) es cuando una API verifica que un ID existe pero nunca chequea si ese ID pertenece a quien lo pide. En el experimento documentado, la función de borrado de tareas no comparaba el userId de la tarea con el usuario que hacía la request, así que cualquiera podía borrar tareas ajenas con un simple ID conocido.

¿Cómo se hace un pentesting ético a mi propia API?

Se hace probando exclusivamente recursos propios, dentro de la política de pentesting de AWS que permite testear infraestructura propia sin autorización previa. El método usado en este caso fue desplegar dos versiones idénticas de la misma API (una sin controles, otra hardened) y atacar ambas desde una terminal propia, documentando cada request y respuesta.

¿Qué es la enumeración en seguridad de APIs?

La enumeración es cuando un endpoint devuelve más datos de los que el solicitante debería ver, típicamente iterando o volcando todos los registros de una base de datos. En el caso analizado, un table.scan() de DynamoDB sin filtro por usuario devolvió las tareas de todos los usuarios en una sola request anónima.

¿Cómo configurar rate limiting en API Gateway de AWS?

El rate limiting se configura como throttling nativo de API Gateway, estableciendo un umbral de requests por unidad de tiempo que, al superarse, devuelve 429 Too Many Requests. En el experimento, la versión sin throttling recibió 5000 requests sin ningún freno diseñado, mientras que la versión hardened rechazó el exceso en el umbral elegido por el desarrollador.

¿Qué es el Shared Responsibility Model de AWS?

El Shared Responsibility Model es el marco de AWS que divide qué asegura el proveedor y qué le corresponde a quien despliega la aplicación. AWS cubre la infraestructura física, el hypervisor y la red; la autenticación, los permisos IAM y el rate limiting de tu API quedan de tu lado de la línea.

Conclusión

Este experimento no revela una falla nueva de AWS: confirma con evidencia reproducible algo que ya se sabía en teoría pero pocas veces se ve en la práctica. Las cuatro vulnerabilidades (enumeración, XSS almacenado, IDOR y abuso de costo) aparecen por default cuando nadie configura nada, y las cuatro se cierran con cambios de código de una o pocas líneas: un authorizer, un regex, una comparación, un umbral de throttling.

Si mantenés una API serverless en AWS, el ejercicio concreto que dejás sacar de acá es simple: probá tu propio endpoint sin token, con un ID ajeno y con una ráfaga de requests, y anotá qué código de respuesta te da. Si alguno de esos tres tests te devuelve un 200 en vez de un 401, un 403 o un 429, ya sabés por dónde empezar a corregir.

Fuentes

Te puede interesar...