GraphQL permite a los clientes especificar exactamente los datos que necesitan en una consulta. Se diferencia de REST en que las consultas pueden estructurarse de manera dinámica.
Un endpoint GraphQL suele responder a peticiones HTTP POST con un payload en formato JSON que contiene:
Probar modificando el método GET/POST y/o tipo de petición x-www-form-urlencoded o application/json.
Cualquier endpoint GraphQL responderá a la consulta universal. Si da la siguiente respuesta, se determinará que se usa GraphQL:
Consulta universal:
{ "query": "{ __typename }" }
Respuesta:
{ "data": { "__typename": "Query" } }
Los servicios GraphQL suelen responder a cualquier solicitud que no sea GraphQL con un error de "consulta no presente" o similar.
Introspección
GraphQL tiene una funcionalidad llamada introspección (introspection) que permite a los desarrolladores (y atacantes) consultar el esquema de la API:
{ "query": "{__schema{queryType{name}}}" }
Si esta consulta está habilitada, se podrá extraer información sobre todos los tipos, consultas y mutaciones disponibles.
Consulta de introspección completa: La siguiente consulta de ejemplo devuelve detalles completos sobre todas las consultas, mutaciones, suscripciones, tipos y fragmentos.
POST:
query IntrospectionQuery {
__schema {
queryType {
name
}
mutationType {
name
}
subscriptionType {
name
}
types {
...FullType
}
directives {
name
description
args {
...InputValue
}
onOperation #Often needs to be deleted to run query
onFragment #Often needs to be deleted to run query
onField #Often needs to be deleted to run query
}
}
}
fragment FullType on __Type {
kind
name
description
fields(includeDeprecated: true) {
name
description
args {
...InputValue
}
type {
...TypeRef
}
isDeprecated
deprecationReason
}
inputFields {
...InputValue
}
interfaces {
...TypeRef
}
enumValues(includeDeprecated: true) {
name
description
isDeprecated
deprecationReason
}
possibleTypes {
...TypeRef
}
}
fragment InputValue on __InputValue {
name
description
type {
...TypeRef
}
defaultValue
}
fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
GET:
/?query=fragment%20FullType%20on%20Type%20{+%20%20kind+%20%20name+%20%2 0description+%20%20fields%20{+%20%20%20%20name+%20%20%20%20description+%20 %20%20%20args%20{+%20%20%20%20%20%20...InputValue+%20%20%20%20}+%20%20 %20%20type%20{+%20%20%20%20%20%20...TypeRef+%20%20%20%20}+%20%20}+%20 %20inputFields%20{+%20%20%20%20...InputValue+%20%20}+%20%20interfaces%20{+%20 %20%20%20...TypeRef+%20%20}+%20%20enumValues%20{+%20%20%20%20name+%20 %20%20%20description+%20%20}+%20%20possibleTypes%20{+%20%20%20%20...TypeRef +%20%20}+}++fragment%20InputValue%20on%20InputValue%20{+%20%20name+%20%2 0description+%20%20type%20{+%20%20%20%20...TypeRef+%20%20}+%20%20defaultValue +}++fragment%20TypeRef%20on%20Type%20{+%20%20kind+%20%20name+%20%20ofT ype%20{+%20%20%20%20kind+%20%20%20%20name+%20%20%20%20ofType%20{+%20 %20%20%20%20%20kind+%20%20%20%20%20%20name+%20%20%20%20%20%20ofTyp e%20{+%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20name+%2 0%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20kind +%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20 %20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20 %20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20%20%2 0ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%2 0%20%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20% 20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20%2 0%20%20%20kind+%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20na me+%20%20%20%20%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20%20%2 0%20%20%20%20%20%20}+%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20 %20%20%20%20}+%20%20%20%20%20%20}+%20%20%20%20}+%20%20}+}++query%20In trospectionQuery%20{+%20%20schema%20{+%20%20%20%20queryType%20{+%20%20 %20%20%20%20name+%20%20%20%20}+%20%20%20%20mutationType%20{+%20%20%2 0%20%20%20name+%20%20%20%20}+%20%20%20%20types%20{+%20%20%20%20%20 %20...FullType+%20%20%20%20}+%20%20%20%20directives%20{+%20%20%20%20%20% 20name+%20%20%20%20%20%20description+%20%20%20%20%20%20locations+%20%20 %20%20%20%20args%20{+%20%20%20%20%20%20%20%20...InputValue+%20%20%20%2 0%20%20}+%20%20%20%20}+%20%20}+}The last code line is a graphql query that will dump all the meta-information from the graphql (objects names, parameters, types...)
GraphQLmap
Explotación de vulnerabilidades
Inyección de consultas
Si un servidor GraphQL no valida correctamente los datos de entrada, un atacante puede inyectar consultas maliciosas.
Este tipo de ataque puede usarse para manipular consultas o acceder a datos no autorizados.
Consulta masiva de datos
GraphQL permite usar alias para ejecutar múltiples consultas en una sola petición, lo que puede usarse para evadir límites de peticiones. Si la API no limita la cantidad de alias, un atacante podría extraer datos en masa.
{
"query": "query { user1: user(id: 1) { name } user2: user(id: 2) { name } user3: user(id: 3) { name } }"
}
Ataques de Denegación de Servicio (DoS) con recursión profunda
Si la API no impone restricciones en la profundidad de las consultas, se pueden hacer consultas recursivas que sobrecarguen el servidor. Esto puede provocar un alto consumo de CPU y memoria en el servidor.
Este ataque es conocido como fuerza bruta con procesamiento por lotes (batch brute-force attack) y aprovecha la flexibilidad de GraphQL para enviar múltiples intentos de autenticación en una sola solicitud HTTP. Esto puede evadir mecanismos de rate-limiting y detección de bots, ya que muchas aplicaciones solo registran el número de solicitudes en lugar del número real de intentos de autenticación dentro de una solicitud.
En GraphQL, se pueden usar alias para enviar múltiples consultas en una sola petición, lo que permite probar múltiples combinaciones de usuario/contraseña sin generar múltiples solicitudes HTTP:
Se están enviando cuatro intentos de autenticación en una sola solicitud HTTP.
Cada intento tiene un alias (login1, login2, etc.) para que GraphQL devuelva múltiples respuestas dentro de un solo JSON.
Si alguna de las credenciales es válida, la API responderá con un token de autenticación.
Medidas de mitigación
Imponer restricciones en el uso de alias en consultas de autenticación. Solo permitir un intento de autenticación por consulta GraphQL.
Aplicar rate-limiting basado en credenciales y no solo en peticiones HTTP. Si una dirección IP envía una solicitud con múltiples intentos de inicio de sesión, se deben contabilizar como intentos separados para la limitación de velocidad.
Implementar retrasos entre intentos de autenticación. Agregar un pequeño tiempo de espera tras intentos fallidos para hacer que los ataques de fuerza bruta sean más lentos.
Bloquear o alertar en caso de múltiples intentos fallidos dentro de una sola solicitud. Si se detectan múltiples intentos de autenticación en una sola consulta, se debe generar una alerta de seguridad.