Security
14 min read
1424 views

Ataques de Batching en GraphQL: Cómo 100 Consultas Se Convierten en 10,000 Llamadas a la Base de Datos 📊

IT
InstaTunnel Team
Published by our engineering team
Ataques de Batching en GraphQL: Cómo 100 Consultas Se Convierten en 10,000 Llamadas a la Base de Datos 📊

Introducción: El Peligro Oculto en la Función Más Conveniente de GraphQL

GraphQL ha revolucionado la forma en que las aplicaciones modernas consultan datos, ofreciendo una flexibilidad y eficiencia sin precedentes. Sin embargo, este poder conlleva una vulnerabilidad de seguridad significativa que muchos desarrolladores pasan por alto: ataques de batching. Lo que parece una sola solicitud HTTP inocente puede transformarse silenciosamente en miles de operaciones en la base de datos, poniendo en riesgo toda tu infraestructura.

En esta guía completa, exploraremos cómo los atacantes explotan la función de batching de GraphQL para amplificar los ataques exponencialmente, por qué permitir entradas en forma de array puede convertirse en una pesadilla de agotamiento de recursos, y lo más importante, cómo proteger tu API de GraphQL contra estos ataques devastadores.

Entendiendo el Batching en GraphQL: Una Espada de Doble Filo

¿Qué es el Batching en GraphQL?

El batching en GraphQL es una función documentada descrita en la especificación de GraphQL publicada en junio de 2018 en la sección 6.3.1, que permite enviar múltiples consultas en una sola solicitud GraphQL. Esta técnica fue diseñada para resolver un problema común: reducir la sobrecarga de red consolidando varias solicitudes de datos en una sola llamada HTTP.

Solo una tanda transita por la red y luego todas las consultas se ejecutan secuencialmente. Aunque esto mejora el rendimiento para usuarios legítimos, crea una superficie de ataque enorme para actores maliciosos.

Dos Tipos de Ataques de Batching

GraphQL soporta dos métodos principales de batching, cada uno con implicaciones de seguridad únicas:

1. Batching Basado en Arrays

El batching basado en arrays permite a los atacantes enviar múltiples operaciones juntas en una sola solicitud HTTP, aunque tiene una gran desventaja: los arrays no están disponibles en todas partes. Cuando son soportados, los atacantes pueden estructurar solicitudes así:

[
  {"query": "mutation { login(username: \"user1\", password: \"pass1\") { token } }"},
  {"query": "mutation { login(username: \"user2\", password: \"pass2\") { token } }"},
  {"query": "mutation { login(username: \"user3\", password: \"pass3\") { token } }"}
]

2. Batching Basado en Alias

Los alias son geniales porque forman parte de la especificación de GraphQL, como se muestra en la sección 2.5, por lo que al encontrar una API GraphQL, deberías esperar que los alias estén disponibles. Sin embargo, los alias tienen una desventaja: solo pueden aplicarse a consultas o mutaciones, pero no a ambas en una sola solicitud.

Los atacantes pueden aprovechar los alias para lograr una forma de batching donde una sola solicitud contiene docenas o incluso cientos de consultas alias que se ejecutan secuencialmente.

La Matemática de la Amplificación del Ataque

De 1 Solicitud a 10,000 Llamadas a la Base de Datos

La naturaleza exponencial de los ataques de batching los hace muy peligrosos. Considera este escenario:

Ataque Tradicional (sin Batching): - 1 solicitud HTTP = 1 consulta en la base de datos - Para probar 10,000 contraseñas = 10,000 solicitudes HTTP - Fácil de detectar por limitadores de tasa y WAFs

Ataque de Batching: - 1 solicitud HTTP = 100-1,000 consultas en la base de datos - Para probar 10,000 contraseñas = 10-100 solicitudes HTTP - Completamente invisible para las herramientas de seguridad tradicionales

Es una forma sencilla de evadir límites de intentos y de tasa. De hecho, los límites suelen establecerse en el número de llamadas API, pero ¿qué pasa si una sola llamada API puede generar 10,000 solicitudes en la base de datos? Las herramientas responsables de asegurar aplicaciones web como firewalls y limitadores de tasa no pueden detectar actividades anómalas: solo monitorean llamadas API.

Impacto en el Mundo Real: La Explosión de Complejidad

Con una llamada de red que se traduce en numerosas consultas o solicitudes de objetos, la comunicación de la aplicación con la base de datos puede conducir a agotamiento de CPU o memoria, volviéndose inaccesible para usuarios legítimos.

El problema se agrava con consultas anidadas. Considera una plataforma de redes sociales donde los usuarios pertenecen a grupos, y los grupos contienen usuarios. Un atacante podría crear una consulta así:

query {
  group(id: "123") {
    users(limit: 100) {
      groups(limit: 100) {
        users(limit: 100) {
          groups(limit: 100) {
            name
          }
        }
      }
    }
  }
}

En solo cuatro niveles de anidamiento con un límite de 100 en cada nivel, esta consulta podría intentar obtener 100 × 100 × 100 × 100 = 100 millones de resultados, causando una carga catastrófica en la base de datos.

Vectores de Ataque: Cómo Se Manifiestan los Ataques de Batching

1. Bypass de Autenticación por Fuerza Bruta

Al combinar múltiples operaciones en una sola solicitud, los atacantes podrían organizar ataques de batching e intentar evadir medidas de seguridad como límites de tasa.

Un atacante podría intentar un ataque de fuerza bruta, usando la función de batching de GraphQL para enviar múltiples consultas en una sola solicitud HTTP, para adivinar la contraseña, y al recibir un token, obtener acceso completo a la API como usuario autenticado.

Ejemplo de Ataque:

[
  {"query": "mutation { login(email: \"victim@example.com\", password: \"password123\") }"},
  {"query": "mutation { login(email: \"victim@example.com\", password: \"123456\") }"},
  {"query": "mutation { login(email: \"victim@example.com\", password: \"qwerty\") }"},
  // ... 997 intentos más
]

Una sola solicitud con 1,000 intentos de inicio de sesión parece una sola llamada HTTP a los sistemas de monitoreo, evadiendo completamente los límites de tasa.

2. Evasión de la Autenticación de Dos Factores (2FA)

Quizá la vulnerabilidad más alarmante es la capacidad de evadir la autenticación de dos factores. Usando el ataque de batching en GraphQL, es posible evadir completamente uno de los factores de autenticación secundaria, OTP (Contraseña de un solo uso), enviando todas las variantes de tokens en una sola solicitud.

Dado que los códigos OTP suelen tener solo 1,000,000 de combinaciones posibles (para códigos de 6 dígitos), un atacante puede: 1. Generar OTP 2. Enviar una solicitud agrupada con todos los códigos posibles 3. Procesar todos los intentos simultáneamente 4. Autenticar con éxito cuando se encuentre el código correcto

La aplicación web vulnerable de GraphQL procesó todos los “tokens de una sola vez” al mismo tiempo, encontró uno válido y permitió que el atacante ingresara.

3. Ataques de Denegación de Servicio (DoS)

GraphQL soporta batching de consultas, que toma una serie de consultas y las envía en una sola tanda. Esto significa que una sola solicitud HTTP contiene varias consultas GraphQL, mientras que en el backend el proceso es uno tras otro. Este procesamiento puede llevar a un DoS a nivel de aplicación, ya que una sola llamada de red se traduce en un alto número de consultas o solicitudes de objetos.

Información de 13,000 problemas en APIs de GraphQL revela que el 80% de los problemas podrían haberse resuelto implementando buenas prácticas como control de acceso con autorización y autenticación, validación de entradas y límites de tasa para bloquear ataques de fuerza bruta.

4. Enumeración y Extracción de Datos

Los ataques de batching introducen vectores adicionales, como ataques de fuerza bruta y enumeración de objetos que apuntan a información de usuarios como nombres, correos electrónicos y cuentas.

Los atacantes pueden enumerar eficientemente: - Cuentas de usuario - Direcciones de email - Identificadores de recursos - Datos privados mediante adivinanza secuencial de IDs

Durante un compromiso reciente, una API construida con GraphQL permitió recuperar Información de Salud del Paciente (PHI) cuando se proporcionaba un “número de secuencia” válido en una consulta GraphQL. Este número de secuencia era generado aleatoriamente por cuenta; sin embargo, no era un UUID largo y podía ser fácilmente enumerado.

Por qué Fallan las Medidas de Seguridad Tradicionales

El Problema del Limitador de Tasa

El limitador de tasa tradicional opera a nivel de solicitudes HTTP, contando el número de llamadas API por período de tiempo. Sin embargo:

Este enfoque engañaría a las aplicaciones de monitoreo de tasa externas, haciéndoles pensar que todo está bien y que no hay bots de fuerza bruta intentando adivinar contraseñas.

Vista de Seguridad Tradicional:

IP del Cliente: 192.168.1.100
Solicitudes por minuto: 5
Estado: ✓ Tráfico normal

Realidad:

IP del Cliente: 192.168.1.100
Solicitudes HTTP: 5
Operaciones reales: 5,000
Consultas a la base de datos: 50,000
Estado: ✗ Ataque activo

El Punto Ciego del WAF y Firewall

Para las herramientas responsables de asegurar aplicaciones web, como los WAFs y RASPs, es difícil identificar actividad anormal en el servidor cuando cada solicitud API puede encapsular miles de solicitudes maliciosas que componen un ataque.

Los WAFs modernos analizan patrones de tráfico HTTP, pero generalmente no pueden analizar en profundidad la semántica de las consultas GraphQL para entender el costo computacional real de cada solicitud.

La Brecha de Monitoreo

Por lo tanto, todos tus mecanismos de protección comunes fallarán en detectar y negar un ataque de fuerza bruta. Así, un atacante puede simplemente hacer una llamada API con cientos de intentos para encontrar credenciales de usuario (email, contraseñas). También pueden evadir la autenticación de dos factores (2FA) enviando todas las variantes de tokens del OTP.

Estrategias de Defensa Integrales

1. Implementar Análisis de Complejidad de Consultas

La complejidad es el número de campos en la consulta. La complejidad predeterminada de cada campo es 1. Sin embargo, debes asignar valores de complejidad personalizados según el costo computacional:

const schema = Schema.build(Query, EmptyMutation, EmptySubscription)
  .limit_complexity(100) // Complejidad máxima permitida
  .finish();

La complejidad de la consulta te permite definir qué tan complejos son estos campos y restringir consultas con una complejidad máxima. La idea es definir qué tan complejo es cada campo usando un número simple.

Ejemplo de Implementación:

field :posts, [PostType], null: false do
  argument :limit, Integer, required: false, default_value: 10
  complexity -(ctx, args, child_complexity) {
    args[:limit] * child_complexity
  }
end

2. Aplicar Limitación en la Profundidad de Consultas

Al restringir la profundidad de las consultas, podemos evitar consultas excesivamente complejas y que consumen muchos recursos.

Esta librería valida la profundidad total de las consultas y mutaciones. Un límite de profundidad generará un error de validación para consultas o mutaciones que excedan la profundidad máxima especificada.

Implementación:

import depthLimit from 'graphql-depth-limit';

app.use('/graphql', graphqlHTTP({
  schema,
  validationRules: [depthLimit(10)]
}));

Por ejemplo, un servidor configurado con una Profundidad Máxima de Consulta de 3, cualquier consulta donde los elementos internos excedan esta profundidad sería inválida.

3. Restringir Límites en Operaciones por Batch

Limitar el número de operaciones que se pueden agrupar y ejecutar a la vez es otra opción para mitigar ataques de batching en GraphQL que llevan a DoS. Sin embargo, esto no es una solución definitiva y debe usarse junto con otros métodos.

Mejores Prácticas: - Limitar batching basado en arrays a 5-10 operaciones como máximo - Restringir el número de alias por consulta (ej., máximo 20 alias) - Implementar límites en tamaño del cuerpo en solicitudes HTTP

4. Desactivar Batching en Operaciones Sensibles

Otra opción es prevenir batching en objetos sensibles que no quieres que sean forzados por fuerza bruta, como nombres de usuario, correos electrónicos, contraseñas, OTPs, tokens de sesión, etc. De esta forma, un atacante se ve obligado a atacar la API como una API REST y hacer una llamada diferente por cada instancia de objeto.

Operaciones Críticas a Proteger: - Mutaciones de autenticación - Operaciones de restablecimiento de contraseña - Verificación OTP - Procesamiento de pagos - Modificación de cuentas

5. Implementar Limitación de Tasa a Nivel de Operación

Ir más allá del límite de tasa a nivel HTTP hacia el nivel de operación:

const rateLimits = {
  login: { max: 5, window: '1m' },
  verifyOTP: { max: 3, window: '5m' },
  resetPassword: { max: 3, window: '1h' }
};

Esto asegura que incluso si las operaciones se agrupan, el conteo acumulado active los límites de tasa.

6. Aplicar Mecanismos de Timeout en Consultas

Existe otra estrategia que consiste en limitar el tiempo de consulta: el “timeout de seguridad”. Ya no se trata solo de bloquear solicitudes demasiado exigentes antes de que se ejecuten, sino de monitorear su tiempo de ejecución y detenerlas si toman demasiado.

Configura tiempos de espera razonables en varios niveles: - Nivel del resolver de GraphQL: 1-2 segundos por resolver - Nivel de ejecución de consulta: 5-10 segundos en total - Nivel de solicitud HTTP: máximo 30 segundos

7. Usar Herramientas de Seguridad para GraphQL

En Escape, creamos GraphQL Armor, un plugin de código abierto para implementaciones de GraphQL en JavaScript, que aporta buenas prácticas de seguridad por defecto a tu API.

Herramientas de Seguridad Recomendadas: - GraphQL Armor: Plugin de seguridad integral - graphql-depth-limit: Biblioteca para limitar profundidad - graphql-query-complexity: Análisis de complejidad - Escape Security Scanner: Detección automática de vulnerabilidades

8. Implementar Validación de Entrada Apropiada

El 80% de los problemas podrían haberse resuelto implementando buenas prácticas como control de acceso con autorización y autenticación, validación de entradas y límites de tasa para bloquear ataques de fuerza bruta.

Lista de Verificación de Validación: - Validar rangos de argumentos (ej., limit: 1-100) - Sanitizar todas las entradas de usuario - Aplicar restricciones en tipos de datos - Rechazar cadenas excesivamente largas - Limitar tamaños de arrays

9. Monitorear y Registrar Patrones de Consultas

Implementa un registro exhaustivo para detectar patrones de ataque:

class LogQueryComplexity {
  result() {
    const complexity = super();
    logger.info(`[GraphQL] Complejidad: ${complexity}, Profundidad: ${depth}, Operaciones: ${operations}`);
  }
}

Alertar sobre patrones sospechosos: - Picos repentinos en la complejidad de consultas - Intentos fallidos repetidos de autenticación - Patrones inusuales de batching - Consultas desde actores maliciosos conocidos

10. Usar Consultas Persistidas en Producción

Se recomienda encarecidamente aceptar solo documentos confiables en tu servidor GraphQL, como hace Facebook internamente.

Implementa una lista blanca de consultas permitidas: - Registrar previamente todas las consultas legítimas - Rechazar consultas ad-hoc en producción - Usar IDs de consulta en lugar de cadenas completas - Mantener control de versiones estricto

Mejores Prácticas de Configuración de Seguridad

Configuración Segura Predeterminada

Por defecto, la mayoría de las implementaciones de GraphQL tienen configuraciones predeterminadas inseguras que deben cambiarse: No devolver mensajes de error excesivos.

Lista de Verificación de Configuración en Producción:

const secureConfig = {
  // Complejidad y profundidad
  maxComplexity: 100,
  maxDepth: 7,
  maxListDepth: 3,
  
  // Límites en batching
  maxBatchSize: 10,
  maxAliases: 15,
  
  // Tiempos de espera
  queryTimeout: 5000,
  resolverTimeout: 1000,
  
  // Funciones de seguridad
  introspection: false, // Desactivar en producción
  playground: false,    // Desactivar en producción
  debug: false,         // Ocultar rastros de pila
  
  // Límites de tasa
  rateLimit: {
    window: '15m',
    max: 100,
    skipSuccessfulRequests: false
  }
};

Manejo de Errores

Las APIs de GraphQL en producción no deben devolver rastros de pila detallados ni estar en modo depuración. Los propietarios de GraphQL deben usar un middleware para controlar los errores que devuelve el servidor. Además, deben enmascarar los errores y hacer que estén disponibles para los desarrolladores, pero no para los llamantes de la API.

Devuelve mensajes de error genéricos a los clientes:

{
  "errors": [{
    "message": "Ocurrió un error al procesar tu solicitud",
    "extensions": {
      "code": "INTERNAL_SERVER_ERROR"
    }
  }]
}

Registra errores detallados en el servidor para depuración.

Pruebas de Seguridad de tu API GraphQL

Lista de Verificación para Pruebas de Seguridad

  1. Pruebas de Ataques de Batching

    • Enviar más de 100 operaciones en una sola solicitud
    • Probar límites de batching basado en arrays
    • Verificar restricciones en alias
  2. Pruebas de Autenticación

    • Intentar evadir OTP con códigos agrupados
    • Probar fuerza bruta de contraseñas mediante batching
    • Verificar protecciones de autenticación multifactor
  3. Simulación de DoS

    • Enviar consultas profundamente anidadas
    • Probar consultas con puntuaciones altas de complejidad
    • Verificar que los mecanismos de timeout se activen
  4. Pruebas de Enumeración

    • Intentar enumerar IDs de usuario
    • Probar validación de direcciones de email
    • Verificar controles de acceso a recursos

Escaneo de Seguridad Automatizado

En Escape, creamos un escáner especializado en APIs de GraphQL. Puede agilizar este proceso identificando rápidamente problemas en tus endpoints y proporcionando fragmentos de remediación, todo en tan solo 15 minutos, sin integraciones complejas ni monitoreo de tráfico.

Usa herramientas automatizadas regularmente: - Escaneos de seguridad semanales - Pruebas previas a despliegues - Monitoreo continuo en producción - Pruebas de penetración periódicas

Estudios de Caso del Mundo Real

Caso 1: Brecha en API de Salud

Durante un compromiso reciente, una API construida con GraphQL permitió recuperar Información de Salud del Paciente (PHI) cuando se proporcionaba un “número de secuencia” válido. Este número se generaba aleatoriamente por cuenta; sin embargo, no era un UUID largo y podía ser fácilmente enumerado.

Método de Ataque: - Números de secuencia de 9 dígitos (10^9 posibilidades) - Batching redujo el ataque de 1 billón de solicitudes a 100,000 - Usando Turbo Intruder con batching optimizado - Enumeró con éxito registros de pacientes

Lección: Nunca confiar solo en identificadores “difíciles de adivinar”. Implementa autenticación adecuada y límites de tasa a nivel de operación.

Caso 2: Plataforma de Comercio Electrónico

Esto se resuelve comúnmente con una técnica de batching, donde múltiples solicitudes de datos se recopilan en un corto período y luego se envían en una sola solicitud a una base de datos o microservicio subyacente.

Una plataforma de comercio electrónico sufrió tomas de control de cuentas mediante ataques de fuerza bruta agrupados: - Los atacantes enviaron 1,000 intentos de contraseña por solicitud HTTP - Los límites de tasa no detectaron el ataque (solo 10 solicitudes HTTP en total) - Se comprometieron más de 500 cuentas antes de detectar - Resultado: más de $2 millones en transacciones fraudulentas

Mitigación: Implementaron límites de tasa a nivel de operación y desactivaron batching en mutaciones de autenticación.

El Futuro de la Seguridad en GraphQL

Estándares Emergentes y Mejores Prácticas

La comunidad de GraphQL trabaja activamente en mejoras de seguridad:

  1. Directivas de Seguridad Estandarizadas

    • Declaraciones de seguridad a nivel de esquema
    • Soporte incorporado para límites de tasa
    • Análisis de complejidad nativo
  2. Mejoras en Herramientas

    • Mejor análisis estático
    • Pruebas de seguridad automatizadas
    • Detección en tiempo real de amenazas
  3. Educación Comunitaria

    • Cursos de seguridad en GraphQL
    • Bibliotecas de seguridad de código abierto
    • Bases de datos de vulnerabilidades compartidas

Conclusión: Equilibrando Poder y Seguridad

Los ataques de batching en GraphQL representan un desafío fundamental en la seguridad moderna de APIs: ¿cómo mantenemos la flexibilidad y potencia que hace atractivo a GraphQL mientras protegemos contra la amplificación exponencial de ataques?

Existe una nueva superficie de ataque cuando la pila tecnológica de la app incluye GraphQL. La primera línea de defensa es la codificación segura. Se debe tener cuidado al seguir la especificación y prestar atención al implementar métodos específicos que puedan requerir filtrado de datos y límites de tasa.

Puntos Clave:

  1. Entiende el Riesgo: Una sola solicitud GraphQL puede activar miles de operaciones en la base de datos
  2. Asegura Capas: Ningún mitigador único es suficiente; usa múltiples estrategias
  3. Monitorea Continuamente: Rastrea la complejidad, profundidad y conteo de operaciones
  4. Prueba Regularmente: La escaneo de seguridad automatizado debe ser parte de tu pipeline CI/CD
  5. Mantente Actualizado: La seguridad en GraphQL evoluciona; sigue las mejores prácticas

Implementando las estrategias de defensa integral descritas en esta guía, puedes aprovechar las potentes funciones de GraphQL mientras proteges tu infraestructura de ataques de batching y agotamiento de recursos. Recuerda: la seguridad no es una configuración única, sino un proceso continuo de monitoreo, prueba y mejora.

La conveniencia del batching en GraphQL no debe costar seguridad; con las salvaguardas adecuadas, puedes tener rendimiento y protección.


Recursos Adicionales

Palabras clave: seguridad en GraphQL, ataques de batching, seguridad API, complejidad de consultas, prevención de DoS, vulnerabilidades en GraphQL, límites de tasa, evasión de autenticación, seguridad 2FA, mejores prácticas en GraphQL

Continue from this article into the most relevant product guides and workflows.

Related Topics

#GraphQL batching attacks, GraphQL security vulnerabilities, GraphQL DoS attack, query complexity GraphQL, GraphQL rate limiting, GraphQL authentication bypass, GraphQL brute force attack, GraphQL security best practices, GraphQL depth limiting, GraphQL API security, array based batching GraphQL, alias based batching GraphQL, GraphQL OTP bypass, GraphQL 2FA bypass, GraphQL resource exhaustion, GraphQL query amplification, GraphQL attack vectors, GraphQL security tools, GraphQL Armor, graphql-depth-limit, GraphQL input validation, GraphQL complexity analysis, GraphQL timeout configuration, GraphQL persisted queries, GraphQL introspection disable, GraphQL production security, GraphQL WAF bypass, GraphQL monitoring, GraphQL vulnerability scanner, GraphQL penetration testing, GraphQL enumeration attacks, GraphQL data scraping, GraphQL mutation security, GraphQL resolver timeout, GraphQL batch size limit, GraphQL alias restriction, GraphQL query depth, GraphQL nested queries, GraphQL database calls, GraphQL performance security, GraphQL API protection, GraphQL authentication security, GraphQL password brute force, GraphQL account takeover, GraphQL security middleware, GraphQL error handling, GraphQL security configuration, GraphQL OWASP, GraphQL security scanning, GraphQL threat detection, GraphQL security testing, GraphQL secure coding, GraphQL API defense, GraphQL query validation, GraphQL operation limiting, GraphQL security plugin, GraphQL attack prevention, GraphQL security compliance, GraphQL backend security, GraphQL infrastructure protection

Keep building with InstaTunnel

Read the docs for implementation details or compare plans before you ship.

Share this article

More InstaTunnel Insights

Discover more tutorials, tips, and updates to help you build better with localhost tunneling.

Browse All Articles