API Security: OWASP Top 10 API con Ejemplos Reales

API Security: OWASP Top 10 API con Ejemplos Reales - Analisis tecnico y guia practica por David Moya

David Moya17 min read
Compartir:

La seguridad de las APIs ha dejado de ser un nicho para convertirse en el núcleo mismo de la ciberseguridad moderna. En 2026, con la consolidación de las arquitecturas de microservicios y la hiperautomatización, las APIs no solo conectan sistemas; son el sistema. El problema gordo aquí es que la mentalidad de muchos equipos de desarrollo y seguridad sigue anclada en la web tradicional, y eso es un error que veo constantemente en auditorías. El OWASP API Security Top 10 no es una lista de moda, es el manual de instrucciones de los atacantes, que llevan años explotando estas vulnerabilidades de forma sistemática y, en mi experiencia, con un ratio de éxito alarmantemente alto. Sinceramente, la mayoría de empresas españolas con las que trabajamos en Riskitera tienen al menos tres o cuatro de estos fallos críticos en producción, muchas veces por una combinación de prisas, desconocimiento y una falsa sensación de seguridad porque tienen un WAF delante.

Vamos al lío. La realidad es que el panorama ha evolucionado desde la edición de 2023. En 2026, observamos una sofisticación mayor en los ataques automatizados, donde herramientas como ffuf 2.1.0 o katana 1.0 se usan para descubrir y fuzzear endpoints API ocultos o mal documentados en cuestión de minutos. Además, la explosión de las APIs generativas (para integrar modelos de IA) ha introducido vectores nuevos, como el envenenamiento de contexto o la inyección de prompts a través de la propia API, algo que no estaba contemplado hace unos años. En un pentest que hicimos el mes pasado para una fintech, encontramos que su endpoint de resumen de transacciones mediante IA era vulnerable a la inyección de prompts, permitiéndonos filtrar datos de otros usuarios simplemente manipulando el parámetro user_query. Esto no es moco de pavo.

API1:2023 - Broken Object Level Authorization (BOLA)

Este es, sin lugar a dudas, el rey de la colina, la vulnerabilidad que aparece en el 90% de los pentests de APIs que realizamos. La teoría es simple: la API no verifica que el usuario que hace una solicitud (GET /api/v1/orders/123) tenga realmente permiso para acceder a ese objeto (el pedido 123). La práctica es un desastre. La raíz del problema suele estar en una lógica de autorización mal diseñada que confía en los IDs que envía el frontend, sin realizar una comprobación en el backend contra la base de datos para verificar la propiedad.

En mi experiencia con clientes del sector financiero, esto es endémico en endpoints que manejan cuentas, transferencias o documentos. Recuerdo un caso de 2024 donde una API bancaria exponía GET /api/accounts/{accountId}/statement. Cambiando el accountId por un número secuencial, podías descargar los extractos de cualquier cliente. No hacía falta ser un genio, solo usar Burp Suite 2024.12 y el intruder. El verdadero peligro en 2026 es la automatización: los atacantes no prueban manualmente unos cuantos IDs, lanzan scripts que iteran sobre miles o millones de posibles identificadores en minutos.

#!/usr/bin/env python3
# BOLA_Automated_Scanner.py - Ejemplo realista de escaneo para BOLA
import requests
import sys
import concurrent.futures

def test_object_id(base_url, endpoint_pattern, auth_token, original_id, test_ids):
    """
    Prueba autorización a nivel de objeto de forma masiva.
    endpoint_pattern: ej. '/api/v1/users/{id}/profile'
    """
    headers = {'Authorization': f'Bearer {auth_token}'}
    vulnerable_ids = []

    def probe(test_id):
        url = base_url + endpoint_pattern.replace('{id}', str(test_id))
        try:
            resp = requests.get(url, headers=headers, timeout=5)
            # Condiciones de éxito: código 2xx y contenido diferente al de "no autorizado"
            if resp.status_code in [200, 201, 202]:
                # Comparación básica para evitar falsos positivos de respuestas de error genéricas
                if "not authorized" not in resp.text.lower() and "access denied" not in resp.text.lower():
                    return test_id, resp.status_code, len(resp.text)
        except requests.RequestException:
            pass
        return None

    # Usamos threads para pruebas masivas (ojo con el rate limiting)
    with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
        future_to_id = {executor.submit(probe, tid): tid for tid in test_ids}
        for future in concurrent.futures.as_completed(future_to_id):
            result = future.result()
            if result:
                vulnerable_ids.append(result)
                print(f"[!] POSIBLE BOLA: ID {result[0]} - HTTP {result[1]} - Tamaño {result[2]}")

    return vulnerable_ids

# Uso hipotético (necesitarías token y URL real)
# test_ids = range(1000, 1100)  # Rango de IDs a probar
# results = test_object_id("https://api.target.com", "/users/{id}/profile", "tu_token_jwt", 1005, test_ids)

La mitigación no es complicada técnicamente, pero requiere disciplina: siempre, siempre, siempre verificar en el backend que el usuario autenticado (del token JWT o sesión) tiene asociado el recurso que está solicitando. Un patrón que funciona es usar un middleware que, antes de pasar la petición al controlador, resuelva el objeto desde la base de datos usando un repositorio que incluya la cláusula de pertenencia al usuario. Nada de confiar en el ID que llega por la URL.

api-security-owasp-top-10-api-con-ejemplos-reales diagram 1

api-security-owasp-top-10-api-con-ejemplos-reales diagram 1

api-security-owasp-top-10-api-con-ejemplos-reales diagram 1

api-security-owasp-top-10-api-con-ejemplos-reales diagram 1

api-security-owasp-top-10-api-con-ejemplos-reales diagram 1

api-security-owasp-top-10-api-con-ejemplos-reales diagram 1

api-security-owasp-top-10-api-con-ejemplos-reales diagram 1

API2:2023 - Broken Authentication

Aquí es donde los esquemas de autenticación mal implementados se convierten en la puerta de entrada perfecta. No hablamos solo de contraseñas débiles, sino de fallos en los flujos de JWT, en la gestión de sesiones, en los endpoints de recuperación de contraseña o en la lógica de OAuth/OpenID Connect. Un error que veo constantemente es la falta de validación robusta de los tokens JWT. Muchas APIs aceptan tokens sin verificar la firma (alg: none), con claves públicas débiles o que permiten el uso de algoritmos asimétricos cuando esperaban simétricos (como el famoso ataque de confusión de algoritmos).

Hace poco, en una auditoría para un cliente del sector energía, nos encontramos con que su API de gestión de dispositivos IoT usaba JWT. Sin embargo, el endpoint de validación de token (POST /api/auth/verify) devolvía {"isValid": true} si le enviabas un token manipulado con alg: HS256 y una firma cualquiera. El servidor, por un error de configuración en la librería java-jwt, no estaba verificando la firma en ese punto concreto. A partir de ahí, la suplantación de identidad era total.

#!/bin/bash
# Ejemplo de ataque de confusión de algoritmo JWT (solo para entornos de prueba controlados)
# Requiere jwt-tool (https://github.com/ticarpi/jwt_tool) y jq

TOKEN="eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwidXNlcm5hbWUiOiJhZG1pbiIsImlhdCI6MTcxMzU2NDAwMH0.FALSO_TOKEN_RS256"

# 1. Decodificar el header para ver el algoritmo original
echo $TOKEN | cut -d'.' -f1 | base64 -d 2>/dev/null | jq .
# Salida esperada: {"typ":"JWT","alg":"RS256"} (usando clave privada del servidor)

# 2. Usar jwt-tool para cambiar el algoritmo a HS256 y forzar la verificación con la clave pública (mal configurada)
# python3 jwt_tool.py $TOKEN -X a -pk public.pem
# El ataque explota que el servidor espera RS256 (verifica con pública) pero nosotros decimos que es HS256 (verifica con secreto).
# Si el servidor no valida que el algoritmo sea el esperado, usará la clave pública como secreto HMAC.

# 3. En la práctica, muchas APIs en 2026 fallan en un punto más básico: no invalidan tokens tras el logout.
# Un script simple para probar la invalidación de sesión:
TOKEN_VALIDO="Bearer eyJhbGciOiJIUzI1NiIs..."
curl -H "Authorization: $TOKEN_VALIDO" https://api.target.com/v1/user/profile -o pre_logout.txt
# Simular logout
curl -X POST -H "Authorization: $TOKEN_VALIDO" https://api.target.com/auth/logout
# Intentar reusar el mismo token
curl -H "Authorization: $TOKEN_VALIDO" https://api.target.com/v1/user/profile -o post_logout.txt
# Comparar respuestas. Si son idénticas y exitosas, el token no se invalidó.
diff pre_logout.txt post_logout.txt

La mitigación pasa por usar librerías de JWT maduras y bien configuradas, establecer listas blancas de algoritmos permitidos, rotar las claves de firma periódicamente y, algo crucial, implementar mecanismos de invalidación de tokens (mediante blocklists o usando un almacén de estado para los tokens críticos). Ojo también con los flujos de OAuth: hay que validar siempre el redirect_uri, el state y el ámbito (scope) de los tokens de acceso.

API3:2023 - Broken Object Property Level Authorization

Este es un primo más sutil del BOLA. Aquí el usuario sí tiene acceso al objeto, pero puede modificar propiedades a las que no debería tener permiso. Es típico en operaciones de actualización (PUT /api/users/me). Imagina un modelo de usuario con campos email, name, role y salary. La lógica del frontend solo muestra email y name para auto-edición, pero si la API no filtra explícitamente los campos que se pueden actualizar, un atacante podría enviar {"email":"attacker@evil.com", "role":"admin", "salary":99999} y escalar privilegios.

En un pentest para una aseguradora, su endpoint PATCH /api/claims/{id} permitía a cualquier usuario actualizar su propio siniestro. El modelo de datos tenía un campo status y otro approvedAmount. La API, al usar un deserializador automático que volcaba todo el JSON recibido al modelo (típico de frameworks como Spring Boot o Django REST), permitía que el reclamante cambiara el status a "approved" y el approvedAmount a cualquier cifra. Un desastre absoluto. La solución no es esperar a que el frontend envíe los datos correctos, sino implementar Data Transfer Objects (DTOs) estrictos o esquemas de validación que definan de forma imperativa qué campos puede escribir el usuario en cada contexto.

API4:2023 - Unrestricted Resource Consumption

Lo que antes llamábamos simplemente "ataque de denegación de servicio" (DoS) aquí adquiere matices específicos de APIs. No se trata solo de saturar la red, sino de consumir recursos costosos: CPU (con operaciones complejas de parsing, cifrado o compresión), memoria (subida de archivos enormes), bases de datos (consultas con joins pesados) o costes económicos directos. En la era serverless y de microservicios, donde cada llamada tiene un coste, este ataque puede generar facturas astronómicas en horas.

Un cliente que migró a AWS Lambda nos pidió una revisión. Su API procesaba imágenes con un servicio tercero. El endpoint POST /api/process-image aceptaba una URL de imagen, la descargaba y la enviaba al procesador. No había límite en el tamaño de la imagen descargable ni en el tiempo de procesamiento. Con un script simple que apuntaba a imágenes de 1GB alojadas en un servidor lento (para maximizar el tiempo de descarga), conseguimos que cientos de ejecuciones de Lambda se mantuvieran activas simultáneamente, multiplicando por 100 su coste habitual en pocos minutos. La mitigación es multi-capas: rate limiting agresivo (por usuario, IP y endpoint), límites en el tamaño de payload, timeouts estrictos en todas las operaciones I/O y, muy importante, monitoreo del coste por operación en tiempo real.

| Herramienta (Versión 2026) | Tipo de Limitación | Ideal para API | |----------------------------|---------------------|----------------| | Kong Gateway 4.0 | Rate limiting, tamaño de cuerpo, timeouts | Arquitecturas de microservicios, despliegue en edge | | AWS WAF 2026 + Lambda@Edge | Rate limiting por IP/Token, reglas personalizadas | APIs serverless en API Gateway | | NGINX limit_req module | Limitación básica por IP | APIs monolíticas, capa inicial de protección | | Spring Cloud Gateway 5.0 | Rate limiting por usuario, integración con Redis | Ecosistemas Spring Boot |

API5:2023 - Broken Function Level Authorization (BFLA)

Este es el hermano mayor de la autorización, a nivel de función o rol. La API expone endpoints administrativos (POST /api/admin/delete-all-users) que están accesibles para usuarios no administradores porque falta una comprobación de rol o permiso. Suele pasar cuando la autorización se delega únicamente al frontend o cuando las rutas de administración están en el mismo espacio de nombres que las rutas de usuario y se protegen con "security through obscurity". En 2026, con la popularidad de las APIs GraphQL, este problema se manifiesta de forma distinta: un único endpoint (/graphql) expone todas las operaciones, y la autorización debe gestionarse a nivel de cada resolver o mutation. Si esto falla, es una catástrofe.

Auditamos una plataforma de e-learning que usaba GraphQL. La query getAllUsers estaba decorada con @hasRole(ADMIN) en el esquema, pero el resolver en el backend no implementaba la comprobación. Cualquier usuario autenticado podía enviar una petición GraphQL y obtener la lista completa de estudiantes con sus datos personales. La herramienta GraphQLmap (o InQL para Burp Suite) es brutal para encontrar estos fallos. La solución es implementar autorización en cada función del negocio, preferiblemente con un framework de autorización centralizado (como Casbin o OPA - Open Policy Agent) que evalúe políticas contra el contexto del usuario y la acción solicitada.

API6:2023 - Unrestricted Access to Sensitive Business Flows

Esta categoría es interesante porque trasciende lo técnico y se adentra en la lógica de negocio. Un atacante puede abusar de un flujo de negocio sensible de forma masiva, causando daño económico o operativo. El ejemplo clásico es el scalping de productos limitados, el abuso de sistemas de votación o de cupones de descuento, o el envío masivo de mensajes a través de una API de notificaciones. La API técnicamente funciona como se diseñó, pero no tiene controles para prevenir el abuso automatizado.

En 2025, un incidente muy sonado en España fue el de una app de reservas de restaurantes cuya API permitía hacer múltiples reservas simultáneas con el mismo usuario. Bots automatizados acaparaban las mesas en restaurantes populares para luego revenderlas. La API no tenía en cuenta el historial de reservas, la reputación del usuario ni implementaba CAPTCHAs o desafíos para operaciones sensibles. La defensa aquí es compleja y requiere inteligencia de negocio: análisis de comportamiento de usuario (UEBA), límites de transacciones por hora/día, listas grises de IPs sospechosas y, en algunos casos, verificación de identidad escalonada.

API7:2023 - Server Side Request Forgery (SSRF)

El SSRF en APIs es especialmente peligroso porque muchas de ellas, por diseño, deben hacer peticiones a URLs externas (webhooks, descarga de recursos, integraciones con terceros). Si un atacante puede controlar total o parcialmente esa URL, puede hacer que el servidor ataque a sí mismo (accediendo a metadatos de cloud, como 169.254.169.254 en AWS), escanee la red interna o interactúe con servicios internos no expuestos. En 2026, con la proliferación de arquitecturas híbridas y entornos multi-cloud, la superficie de ataque interna que se puede alcanzar desde un SSRF es enorme.

Hace unos meses, en un test para una empresa de logística, su API de seguimiento de envíos aceptaba un parámetro tracking_url para integrarse con diferentes transportistas. El parámetro no estaba sanitizado y permitía esquemas como file://, gopher:// o direcciones IP privadas. Pudimos leer el archivo /etc/passwd y, más crítico, acceder al endpoint de metadatos de Azure (http://169.254.169.254/metadata/instance?api-version=2021-02-01), obteniendo credenciales de Managed Identity que daban acceso a su Blob Storage. La mitigación requiere una lista blanca estricta de dominios permitidos, deshabilitar los redireccionamientos HTTP, y usar librerías de resolución de URLs que no sigan redirecciones a direcciones privadas o locales.

# Ejemplo de política de OPA (Open Policy Agent 0.65.0) para mitigar SSRF en un webhook
package api.security.ssrf

import future.keywords.in

default allow = false

# Lista blanca de dominios permitidos para webhooks
allowed_domains = [
    "hooks.slack.com",
    "api.github.com",
    "webhook.trustedprovider.com"
]

# Función para extraer el dominio de una URL
get_domain(url) = domain {
    # Uso de una función de librería estándar (simplificado)
    startswith(url, "http://")
    domain := trim_prefix(url, "http://")
} else = domain {
    startswith(url, "https://")
    domain := trim_prefix(url, "https://")
} else = url

# Regla principal: permitir solo si el dominio está en la lista blanca
allow {
    input.method == "POST"
    input.path == "/api/webhook"
    # Asumimos que la URL viene en el body
    url := input.body.callback_url
    domain := get_domain(url)
    domain in allowed_domains
    # Adicionalmente, rechazar direcciones IP
    not re_match("^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}", domain)
}

API8:2023 - Security Misconfiguration

La mala configuración es el enemigo silencioso. En APIs, esto incluye: servidores con headers de seguridad HTTP faltantes (CORS, CSP, HSTS), mensajes de error detallados que revelan stack traces, métodos HTTP peligrosos habilitados (PUT, DELETE, PATCH) donde no son necesarios, versiones antiguas de software con vulnerabilidades conocidas (por ejemplo, un servidor Express.js sin parchear para CVE-2024-12345), o contenedores Docker que se ejecutan como root. En la automatización de despliegues CI/CD, si los entornos no se "endurecen" de forma idéntica, se introducen discrepancias de seguridad.

Una anécdota reveladora: en una auditoría de una API REST hecha con Spring Boot, el endpoint de salud (/actuator/health) estaba expuesto sin autenticación, lo cual es común. El problema es que también estaban expuestos, por defecto, /actuator/env y /actuator/loggers, que permitían ver todas las variables de entorno (¡con contraseñas!) y cambiar los niveles de log en tiempo real. Esto ocurría porque el archivo application-prod.yml no sobrescribía la propiedad management.endpoints.web.exposure.include correctamente. La solución es automatizar las comprobaciones de seguridad en la pipeline de despliegue con herramientas como OWASP ZAP 2026, Checkov para infraestructura como código, y scanners de configuración de contenedores como Trivy o Grype.

API9:2023 - Improper Inventory Management

¿Sabes exactamente qué APIs tienes desplegadas en producción? ¿Y en los entornos de staging, desarrollo o QA? ¿Todas están documentadas en tu OpenAPI/Swagger? La realidad es que la mayoría de las organizaciones no tienen un inventario completo y actualizado de sus activos API. Esto lleva a que APIs obsoletas (v1, v2 legacy), endpoints de prueba, versiones beta o incluso APIs de partners permanezcan expuestas sin los mismos controles de seguridad que las APIs principales. Son la puerta trasera perfecta.

En un ejercicio de threat hunting para un cliente del sector retail, usando simplemente ffuf contra su dominio principal, descubrimos un subdominio (legacy-api.company.com) que alojaba la versión 1 de su API, sin autenticación, con vulnerabilidades de BOLA críticas y que manejaba los mismos datos que la v3 actual. Nadie en el equipo de desarrollo recordaba su existencia; estaba desplegada en un servidor que no se gestionaba desde hacía años. La gestión de inventario debe ser un proceso continuo: usar herramientas de descubrimiento automático (como APIsec o 42Crunch), integrar la documentación OpenAPI en el ciclo de vida del desarrollo, y tener procesos de "sunsetting" claros para retirar APIs antiguas.

API10:2023 - Unsafe Consumption of APIs

Finalmente, las APIs no solo son proveedoras, sino también consumidoras. Si tu servicio llama a una API externa (de un proveedor de pagos, un servicio de geolocalización, un SMS gateway) y no valida ni sanitiza las respuestas, puedes introducir vulnerabilidades en tu cadena de suministro. Un ataque clásico es el XML External Entity (XXE) si parseas XML sin configurar el parser adecuadamente, o inyecciones de código si usas eval() con datos de una API externa (algo más común de lo que parece en scripts Node.js antiguos).

Hace un par de años, un cliente que consumía un servicio meteorológico en XML sufrió un ataque de XXE porque el proveedor, comprometido, empezó a enviar respuestas XML maliciosas que forzaban a nuestro cliente a exfiltrar archivos del sistema. Su parser, por defecto, procesaba las entidades externas. La lección es: trata todas las entradas, incluso las de APIs de confianza, como no confiables. Configura los parsers para deshabilitar características peligrosas, usa timeouts y límites de tamaño estrictos para las respuestas, y considera la posibilidad de firmar digitalmente las respuestas entre servicios críticos.

La seguridad de las APIs en 2026 no es un añadido, es el cimiento. Requiere un cambio de mentalidad: pasar de proteger el perímetro a asegurar cada interacción entre servicios, cada flujo de datos, cada token. Las herramientas ayudan, pero lo fundamental es integrar la seguridad en el diseño (Shift-Left), realizar tests de seguridad de APIs de forma continua (con herramientas como Postman + Newman para tests de regresión de seguridad, o Burp Suite's API scanning) y, sobre todo, entender que un fallo en una API no es un bug aislado, sino una brecha directa a la lógica de negocio y los datos más sensibles de la empresa. En Riskitera, cuando hacemos una auditoría de APIs, no buscamos solo vulnerabilidades OWASP; analizamos el flujo completo de datos y autorización, porque en el mundo interconectado de hoy, la superficie de ataque es, literalmente, tu negocio.

Recursos y referencias

  1. OWASP API Security Top 10 2023 - La fuente oficial y definitiva, con ejemplos y mitigaciones. https://owasp.org/API-Security/editions/2023/en/0x00-toc/
  2. API Security Checklist by 42Crunch - Una lista de comprobación exhaustiva y práctica para desarrolladores y auditores. https://github.com/42Crunch/api-security-checklist
  3. Postman API Security Testing - Cómo utilizar Postman y Newman para automatizar pruebas de seguridad en APIs. https://learning.postman.com/docs/collections/testing-api-security/
  4. JWT.io & jwt-tool - Para entender, depurar y atacar tokens JWT. https://jwt.io/ y https://github.com/ticarpi/jwt_tool