CodeGym /Cursos /Módulo 5. Spring /Seguridad en GraphQL

Seguridad en GraphQL

Módulo 5. Spring
Nivel 16 , Lección 5
Disponible

GraphQL, como un mecanismo universal de consultas, abre al cliente el acceso a los datos a través de un único punto de entrada — el GraphQL API. Esto simplifica la arquitectura, pero complica la seguridad:

  • Punto único de fallo. Si ese punto se compromete, un atacante podría acceder a todo el sistema.
  • Flexibilidad de las consultas. El usuario puede pedir demasiados datos (o anidamientos demasiado profundos) en una sola consulta.
  • Recursos sin límites. GraphQL permite a los clientes elegir cuántos datos necesitan, lo que puede llevar a abusos de los recursos del servidor.

Por tanto, necesitamos proteger nuestros datos contra accesos no autorizados, prevenir la sobrecarga del servidor y asegurar el cumplimiento de las políticas de acceso.


Amenazas potenciales en GraphQL

  1. Denegación de servicio (DoS): por ejemplo, un ataque a la disponibilidad donde el cliente solicita datos enormemente anidados (o consultas cíclicas).
  2. Violación de la privacidad de los datos: si los usuarios pueden acceder a datos a los que no tienen permiso.
  3. Mutaciones no autorizadas: un atacante puede intentar modificar datos sin autorización.
  4. Inyecciones a través de consultas GraphQL: aunque GraphQL por naturaleza protege contra SQL injections, las consultas dinámicas pueden arruinarlo todo.

Aspectos principales de la seguridad en GraphQL

  1. Autenticación. ¿Quién eres? Es el proceso de verificar la identidad del usuario.
  2. Autorización. ¿Qué puedes hacer? Es la comprobación de permisos para ejecutar acciones concretas.
  3. Restricciones de consultas. Son herramientas que protegen al servidor de la sobrecarga.
  4. Protección del esquema. Eliminamos filtraciones de información sobre la arquitectura del API.

Control de acceso en GraphQL

El enfoque principal es usar el contexto (context) de la petición. El contexto es un objeto que se pasa a cada consulta. Puede contener información sobre el usuario actual, su rol, el token de acceso y otros parámetros.

Ejemplo de configuración del contexto para GraphQL:


@Bean
public GraphQL graphQL(GraphQLSchema schema) {
    return GraphQL.newGraphQL(schema)
            .defaultDataFetcherExceptionHandler(new CustomExceptionHandler())
            .instrumentation(new ContextSettingInstrumentation()) // Contexto para las peticiones
            .build();
}

public class ContextSettingInstrumentation extends SimpleInstrumentation {
    @Override
    public InstrumentationContext<ExecutionInput> beginExecution(InstrumentationExecutionParameters parameters) {
        ExecutionInput executionInput = parameters.getExecutionInput();
        Map<String, Object> context = new HashMap<>();
        context.put("currentUser", fetchCurrentUserFromSecurityContext()); // Añadimos el usuario
        executionInput.transform(builder -> builder.context(context));
        return super.beginExecution(parameters);
    }
}

Autenticación con JWT

Usar JWT (JSON Web Token) permite verificar si el usuario realmente tiene derecho de acceso.

1. Configuramos SecurityContext en Spring Security:


@Override
protected void configure(HttpSecurity http) throws Exception {
    http
      .authorizeRequests()
      .anyRequest().authenticated()
      .and()
      .oauth2ResourceServer()
      .jwt();
}

2. Comprobamos el usuario en el Data Fetcher de GraphQL:


public DataFetcher<String> getSecretInfo() {
    return environment -> {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !isUserAuthenticated(authentication)) {
            throw new AccessDeniedException("Unauthorized");
        }
        return "¡Aquí tu contenido secreto!";
    };
}

Protección de datos y del esquema

Limitación de la profundidad de las consultas

Una de las formas más sencillas de atacar un GraphQL API es crear consultas con alto nivel de anidamiento, por ejemplo:


query {
  user {
    friends {
      friends {
        friends {
          friends {
            name
          }
        }
      }
    }
  }
}

Para prevenir esto usamos la librería graphql-java-servlet, que permite configurar límites de profundidad de consulta.

Ejemplo de añadir la limitación:


GraphQLSchema schema = SchemaGenerator.newBuilder()
        .query(queryType)
        .additionalDirective(graphql.schema.idl.SchemaPrinter.Options.defaultOptions().maxQueryDepth(10))
        .build();

Limitación de la complejidad de las consultas

La complejidad de una consulta es la suma de los "pesos" de todos los campos solicitados. Cuantos más datos anidados se pidan, mayor es la complejidad. El peso de los campos se puede definir manualmente:


public class CustomFieldComplexityCalculator implements FieldComplexityCalculator {
    @Override
    public int calculate(FieldComplexityEnvironment environment, int childComplexity) {
        if ("user".equals(environment.getField().getName())) {
            return 5 + childComplexity; // Peso del campo "user" - 5
        }
        return 1 + childComplexity;
    }
}

Después de esto configuramos GraphQL:


GraphQL.newGraphQL(schema)
       .instrumentation(new MaxQueryComplexityInstrumentation(50)) // Límite de complejidad de consultas
       .build();

Aplicación de autorización a nivel de esquema

Usa anotaciones para limitar el acceso a campos concretos:


@GraphQLField
@PreAuthorize("hasRole('ADMIN')")
public String getAdminData() {
    return "¡Solo para administradores!";
}

Spring Security comprobará automáticamente el rol del usuario antes de ejecutar el método.


Práctica: configurar la seguridad del GraphQL API

Paso 1: Configura Spring Security.

Añade las dependencias en pom.xml:


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

Paso 2: Configura JWT en Spring Boot.

Añade la configuración en application.yml:


spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://your-issuer.com

Paso 3: Añade la comprobación de acceso en los Data Fetchers.

Integra la comprobación de permisos de los usuarios en los manejadores de consultas GraphQL:


public DataFetcher<String> getSecureInfo() {
    return environment -> {
        if (!environment.getContext().get("isAuthenticated")) {
            throw new AccessDeniedException("Access Denied");
        }
        return "Datos secretos para usuarios autenticados";
    };
}

Paso 4: Limita la profundidad y complejidad de las consultas.

Añade en la configuración de GraphQL límites a la profundidad y complejidad:


GraphQL.newGraphQL(schema)
       .instrumentation(new MaxQueryDepthInstrumentation(10))
       .instrumentation(new MaxQueryComplexityInstrumentation(50))
       .build();

Conclusión

Si la seguridad fuera una tarta, ¡acabamos de añadir suficientes ingredientes para seguir la receta! Ahora tu GraphQL API está protegido contra peticiones excesivas, accesos no autorizados y posibles ataques. Pero recuerda que la seguridad no es un proceso de "hacerlo una vez y olvidarlo". Revisa regularmente tus configuraciones, actualiza las librerías y prueba vulnerabilidades.

¡Pasemos al siguiente paso!

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION