JavaRush /Курсы /Модуль 5. Spring /Безопасность в GraphQL

Безопасность в GraphQL

Модуль 5. Spring
30 уровень , 5 лекция
Открыта

GraphQL, как универсальный механизм запросов, открывает для клиента доступ к данным через единую точку входа — GraphQL API. Это упрощает архитектуру, но делает безопасность сложнее:

  • Одиночная точка отказа. Если эта точка скомпрометирована, злоумышленник может получить доступ ко всей системе.
  • Гибкость запросов. Пользователь может запрашивать слишком много данных (или слишком глубокие вложенности) за один запрос.
  • Неограниченные мощности. GraphQL позволяет клиентам сами выбирать, сколько данных нужно, что может привести к злоупотреблению ресурсами сервера.

Соответственно, нам нужно защитить наши данные от несанкционированного доступа, предотвратить перегрузку сервера и обеспечить соблюдение политик доступов.


Потенциальные угрозы в GraphQL

  1. Денялы вне закона: например, атака на производительность, где клиент запрашивает огромные вложенные данные (или циклические запросы).
  2. Нарушение приватности данных: если пользователи могут получить доступ к данным, к которым они не имеют прав.
  3. Несанкционированные мутации: злоумышленник может попытаться изменить данные без авторизации.
  4. Инъекции через GraphQL-запросы: хотя GraphQL по своей природе защищает от SQL-инъекций, динамические запросы могут всё испортить.

Основные аспекты безопасности в GraphQL

  1. Аутентификация. Кто ты? Это процесс подтверждения личности пользователя.
  2. Авторизация. Что тебе можно делать? Это проверка прав для выполнения определённых действий.
  3. Ограничения запросов. Это инструменты, которые защищают сервер от перегрузки.
  4. Защита схемы. Устраняем утечки информации об архитектуре API.

Управление доступом в GraphQL

Основной подход — использование контекста (context) запроса. Контекст — это объект, который передаётся в каждый запрос. Он может содержать информацию о текущем пользователе, его роли, токене доступа и других параметрах.

Пример настройки контекста для GraphQL:


@Bean
public GraphQL graphQL(GraphQLSchema schema) {
    return GraphQL.newGraphQL(schema)
            .defaultDataFetcherExceptionHandler(new CustomExceptionHandler())
            .instrumentation(new ContextSettingInstrumentation()) // Контекст для запросов
            .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()); // Добавляем пользователя
        executionInput.transform(builder -> builder.context(context));
        return super.beginExecution(parameters);
    }
}

Аутентификация через JWT

Использование JWT (JSON Web Token) позволяет проверить, действительно ли пользователь имеет право на доступ.

1. Настраиваем SecurityContext в Spring Security:


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

2. Проверяем пользователя в GraphQL Data Fetcher:


public DataFetcher<String> getSecretInfo() {
    return environment -> {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !isUserAuthenticated(authentication)) {
            throw new AccessDeniedException("Unauthorized");
        }
        return "Здесь ваш секретный контент!";
    };
}

Защита данных и схемы

Ограничение глубины запросов

Одним из самых простых способов атак на GraphQL API является создание запросов с высокой вложенностью, например:


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

Для предотвращения этого используем библиотеку graphql-java-servlet, которая позволяет настраивать лимиты глубины запросов.

Пример добавления ограничения:


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

Ограничение сложности запросов

Сложность запроса — это сумма "весов" всех запрашиваемых полей. Чем больше вложенных данных запрашивается, тем выше сложность. Вес полей можно задать вручную:


public class CustomFieldComplexityCalculator implements FieldComplexityCalculator {
    @Override
    public int calculate(FieldComplexityEnvironment environment, int childComplexity) {
        if ("user".equals(environment.getField().getName())) {
            return 5 + childComplexity; // Вес поля "user" - 5
        }
        return 1 + childComplexity;
    }
}

После этого конфигурируем GraphQL:


GraphQL.newGraphQL(schema)
       .instrumentation(new MaxQueryComplexityInstrumentation(50)) // Лимит сложности запросов
       .build();

Применение авторизации на уровне схемы

Используйте аннотации, чтобы ограничить доступ к определённым полям:


@GraphQLField
@PreAuthorize("hasRole('ADMIN')")
public String getAdminData() {
    return "Только для администраторов!";
}

Spring Security при этом автоматически проверяет роль пользователя перед выполнением метода.


Практика: настройка безопасности GraphQL API

Шаг 1: Настройте Spring Security.

Добавьте зависимости в 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>

Шаг 2: Настройте JWT в Spring Boot.

Добавьте настройки в application.yml:


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

Шаг 3: Добавьте проверку доступа в Data Fetchers.

Интегрируйте проверку прав пользователей в обработчики GraphQL-запросов:


public DataFetcher<String> getSecureInfo() {
    return environment -> {
        if (!environment.getContext().get("isAuthenticated")) {
            throw new AccessDeniedException("Access Denied");
        }
        return "Секретные данные для авторизованных пользователей";
    };
}

Шаг 4: Ограничьте глубину и сложность запросов.

Добавьте в конфигурацию GraphQL ограничения на глубину и сложность:


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

Заключение

Если бы безопасность была пирогом, то мы только что добавили достаточно ингредиентов, чтобы исполнить рецепт! Теперь ваш GraphQL API защищён от излишних запросов, несанкционированного доступа и возможных атак. Но помните, что безопасность — это не процесс типа "один раз сделал и забыл". Не забывайте регулярно пересматривать свои конфигурации, обновлять библиотеки и тестировать уязвимости.

Переходим к следующему шагу!

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ