Leaders Logo

Depth Control in GraphQL APIs with .NET: A Secure Approach to Mitigating DoS Attacks

Introduction to GraphQL

GraphQL is a query language developed by Facebook that allows clients to request exactly the data they need, minimizing the overhead of unnecessary data (GRAPHQL, 2025). Unlike REST APIs, where endpoints are fixed, in GraphQL clients can define the structure of the response, resulting in greater flexibility and efficiency (SABBAG FILHO, 2025). However, this flexibility brings significant challenges, especially in terms of security and performance. In this article, we will discuss how to implement depth controls in GraphQL APIs using .NET, aiming to mitigate denial of service (DoS) attacks.

Security Challenge in GraphQL APIs

One of the main security challenges in GraphQL APIs is the vulnerability to denial of service (DoS) attacks. Due to its flexible nature, a client can make requests that require an excessive amount of server resources, resulting in performance degradation or even service interruptions (MCFADDEN et al., 2024). It is crucial to implement control measures to mitigate these risks and ensure that the API remains responsive and secure. Additionally, the complexity of queries can increase exponentially, making it difficult to predict the impact on performance. Therefore, it is essential for developers to understand these vulnerabilities and implement appropriate solutions.

Depth Control in Queries

One approach to mitigate DoS attacks is depth control in GraphQL queries (MOLLER et al., 2024). This control limits the depth of nested queries, preventing a client from making requests that could overload the server. For example, if a query allows a depth of 5, a client cannot make a request that nests queries more than 5 levels deep. This technique is critical to prevent malicious or malformed queries from overwhelming the system. In addition to limiting depth, it is important to consider the balance between functionality and security, ensuring that legitimate users can access the data they need without unnecessary obstacles.

SVG Image of the Article

Depth Control with .NET

To implement depth control in a GraphQL API using .NET, we can use the GraphQL.NET package (GRAPHQL.NET, 2025). Below, we present an example of how to set up this control:

using GraphQL;
using GraphQL.Types;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<ISchema, MySchema>();
        services.AddGraphQL(options =>
        {
            options.EnableMetrics = false;
            options.ExposeExceptions = false;
            options.FieldMiddleware.Use<DepthControlMiddleware>();
        });
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseGraphQL<ISchema>();
    }
}

public class DepthControlMiddleware : IFieldMiddleware
{
    public async Task<object> Resolve(ResolveFieldContext context, FieldMiddlewareDelegate next)
    {
        var depth = GetQueryDepth(context);
        if (depth > 5) // Depth limit
        {
            throw new Exception("Query too deep. Depth limit of 5 exceeded.");
        }

        return await next(context);
    }

    private int GetQueryDepth(ResolveFieldContext context)
    {
        // Logic to calculate the depth of the query
        // Returns an integer representing the depth
    }
}

Calculating Query Depth by Nesting Level

To calculate the depth of a GraphQL query, you can implement a function that traverses the query tree and counts the nesting level. A simple example can be seen below:

private int GetQueryDepth(ResolveFieldContext context)
{
    int depth = 0;
    var currentField = context.FieldAst;

    while (currentField != null)
    {
        depth++;
        currentField = currentField.SelectionSet?.Selections.OfType<Field>()
        .FirstOrDefault()?.SelectionSet;
    }

    return depth;
}

Calculating Query Complexity Based on Field Weights

Another approach is to use middleware to control the complexity of queries based on the fields used. Each field is assigned a weight, and if the total weight of the query exceeds a predefined limit, it is rejected. The implementation is similar to depth control, with the difference being that specific weights per field are considered.

public class ComplexityControlMiddleware : IFieldMiddleware
{
    private const int MaxComplexity = 100;

    public async Task<object> Resolve(ResolveFieldContext context, FieldMiddlewareDelegate next)
    {
        var complexity = CalculateComplexity(context);
        if (complexity > MaxComplexity)
        {
            throw new Exception("Query complexity exceeded.");
        }

        return await next(context);
    }

    private int CalculateComplexity(ResolveFieldContext context)
    {
        // Logic to calculate the complexity of the query
        // Returns an integer representing the total complexity
    }
}

The next step is to implement the complexity calculation considering the weights of each field. Below is a simple example:

private int CalculateComplexity(ResolveFieldContext context)
{
    int complexity = 1; // Default weight for the current field

    foreach (var selection in context.FieldAst.SelectionSet.Selections)
    {
        if (selection is Field field)
        {
            complexity += CalculateFieldComplexity(field);
        }
    }

    return complexity;
}

private int CalculateFieldComplexity(Field field)
{
    // Assign weights to specific fields
    switch (field.Name.Value)
    {
        case "user":
            return 5; // Example weight
        case "posts":
            return 3; // Example weight
        default:
            return 1; // Default weight
    }
}

Testing the Implementation

After implementing depth control and complexity limits, it is important to perform tests to ensure that the protections work as expected. You can use load testing tools to simulate multiple requests and check if the API responds appropriately without failures or performance degradation. Stress tests are also recommended to evaluate how the API behaves under extreme conditions, helping to identify weaknesses before they become issues in production.

Conclusion

Depth control and complexity limiting are effective approaches to protect GraphQL APIs against DoS attacks. By implementing these measures in your .NET applications, you not only improve security but also ensure a more stable user experience. With the growing adoption of GraphQL, it is crucial for developers to be aware of the risks and implement best practices to protect their APIs. Additionally, documentation and ongoing education about security practices in GraphQL are essential to ensure that the community remains informed about the latest threats and solutions.

References

  • GRAPHQL. Learn GraphQL. Available at: https://graphql.org/learn/. Accessed on: June 1, 2025. reference.Description
  • GRAPHQL.NET. GraphQL.NET - Official Site. Available at: https://dotnetgraphql.com/. Accessed on: June 1, 2025. reference.Description
  • SABBAG FILHO, Nagib. API modeling with REST and GraphQL: Case study in .NET projects. Leaders Tec, v. 2, n. 19, 2025. reference.Description
  • MCFADDEN, Shae et al. WENDIGO: Deep Reinforcement Learning for Denial-of-Service Query Discovery in GraphQL. In: 2024 IEEE Security and Privacy Workshops (SPW). IEEE, 2024. p. 68-75. reference.Description
  • MOLLER, Arno; MAKURA, Sheunesu; VENTER, Hein. A Model to Limit Batching Denial of Service Attacks on GraphQL. In: 2024 IST-Africa Conference (IST-Africa). IEEE, 2024. p. 1-11. reference.Description
About the author