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

Безпека в GraphQL

Модуль 5. Spring
Рівень 16 , Лекція 5
Відкрита

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

  • Єдина точка відмови. Якщо ця точка скомпрометована, зловмисник може отримати доступ до всієї системи.
  • Гнучкість запитів. Користувач може запитувати надто багато даних (або надто глибокі вкладення) в одному запиті.
  • Необмежені ресурси. GraphQL дозволяє клієнтам самим обирати, скільки даних потрібно, що може призвести до зловживання ресурсами сервера.

Відповідно, нам потрібно захистити наші дані від несанкціонованого доступу, запобігти перевантаженню сервера і забезпечити дотримання політик доступу.


Потенційні загрози в GraphQL

  1. Відмови в обслуговуванні (DoS): наприклад, атака на продуктивність, коли клієнт запитує величезні вкладені дані (або циклічні запити).
  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 захищений від надмірних запитів, несанкціонованого доступу та можливих атак. Але пам'ятайте, що безпека — це не процес "зробив один раз і забув". Регулярно переглядайте свої конфігурації, оновлюйте бібліотеки та тестуйте на вразливості.

Переходимо до наступного кроку!

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ