HIGH aspnetgraphql batching

Graphql Batching in Aspnet

How GraphQL Batching Manifests in ASP.NET

In ASP.NET applications using GraphQL (typically via libraries like GraphQL.NET), batching allows multiple GraphQL operations to be sent in a single HTTP request, usually as an array of query objects. While intended for performance optimization, this feature creates a significant attack surface when not properly constrained. The ASP.NET GraphQL server processes each operation in the batch sequentially or in parallel, depending on configuration, against the same underlying data resolvers and authorization logic.

Attack Pattern 1: Query Flooding & Rate Limit Bypass
An attacker sends a batch containing dozens or hundreds of queries, each targeting a different resource ID (e.g., user profiles, orders). Because the batch is a single HTTP request, traditional rate limiting (often configured per-request, not per-operation) may count it as one request, allowing excessive data extraction. In an ASP.NET controller using GraphQL.NET, this might look like:

[HttpPost("graphql")]
public async Task Post([FromBody] GraphQLRequest request)
{
    var result = await _executor.ExecuteAsync(request);
    return Ok(result);
}

// Attacker's batch payload
[
  { "query": "query { user(id: 1) { email } }" },
  { "query": "query { user(id: 2) { email } }" },
  // ... 500 more
]

Attack Pattern 2: Nested Batched Queries (BOLA/IDOR Amplification)
An attacker can combine batching with field-level authorization weaknesses. For example, a batch might include queries that each request a list of sensitive records (e.g., { orders { items { creditCardLast4 } } }) for different customer IDs, exploiting a missing property-level check on the creditCardLast4 field. The ASP.NET resolver for Order might not validate that the requesting user owns each specific order in a high-volume batch, leading to mass data exposure.

Attack Pattern 3: Cost Exploitation via Complex Batches
Each query in a batch can be computationally expensive (e.g., deep recursion, large first: 1000 pagination). A batch of such queries can overwhelm the ASP.NET server's thread pool or database connection pool, causing a denial-of-service. The lack of per-operation cost analysis in the default GraphQL.NET pipeline makes this easy to exploit.

ASP.NET-Specific Detection with middleBrick

middleBrick's black-box scanner tests the unauthenticated attack surface of your ASP.NET GraphQL endpoint by sending crafted batched requests and analyzing the responses. It does not require access to your source code or configuration.

Detection Methodology:

  • Batched Request Injection: middleBrick sends a POST request to /graphql (or your configured endpoint) with a JSON array containing multiple queries. It varies the batch size (e.g., 5, 50, 200 operations) and observes response patterns.
  • Uniform Response Analysis (BOLA/IDOR): It looks for identical response structures across batch items that indicate successful data retrieval for different IDs. For example, if all 200 queries return a user object with an email field, it flags potential Broken Object Level Authorization (BOLA). The scanner cross-references this with the OpenAPI/Swagger spec if available to understand expected data models.
  • Rate Limit & Performance Degradation: By measuring response times for batches of varying sizes, middleBrick detects the absence of effective rate limiting. A linear or sublinear increase in latency when doubling batch size suggests no per-operation throttling.
  • LLM/AI Security (If Applicable): For ASP.NET endpoints serving LLMs (e.g., via Microsoft.SemanticKernel integrations), middleBrick's unique probes include sending batched prompt injection attempts to test for system prompt leakage or cost exploitation through multiple concurrent interactions.

The scan runs in 5–15 seconds and produces a risk score (0–100, A–F) with per-category breakdowns. For a vulnerable ASP.NET GraphQL batch endpoint, you'll typically see high-severity findings in the BOLA/IDOR and Rate Limiting categories, with remediation guidance specific to GraphQL.NET.

ASP.NET-Specific Remediation Strategies

Remediation focuses on implementing validation and throttling at the ASP.NET application layer, using native GraphQL.NET features and middleware. middleBrick's reports provide step-by-step guidance mapped to your codebase's patterns.

1. Enforce Strict Batch Size Limits
Configure the GraphQL.NET execution options to reject batches exceeding a safe threshold (e.g., 10 operations). This is set in your Startup.cs or program configuration:

services.AddGraphQL(options =>
{
    options.ExecutionOptions =
    {
        // Reject batches with more than 10 operations
        BatchSize = 10,
        // Enable per-operation validation
        EnableMetrics = true
    };
});

2. Implement Custom Validation Rules for BOLA/IDOR
Create a validation rule that inspects each operation in a batch for unauthorized ID access. This rule runs during the validation phase before execution:

public class BatchAuthorizationValidationRule : IValidationRule
{
    public ValueTask ValidateAsync(ValidationContext context)
    {
        return new ValueTask(new EnterLeaveListener(_ =>
        {
            _.Match(field =>
            {
                var argument = field.Arguments.FirstOrDefault(a => a.Name == "id");
                if (argument != null && argument.Value is IValue valueNode)
                {
                    var requestedId = valueNode.Value?.ToString();
                    // Check if current user is authorized for this ID
                    if (!UserCanAccessId(context.UserContext, requestedId))
                    {
                        context.ReportError(new ValidationError(
                            context.Document.Source,
                            field,
                            "BATCH_BOLA",
                            $"Unauthorized access to ID '{requestedId}' in batched operation."));
                    }
                }
            });
        }));
    }

    private bool UserCanAccessId(IServiceProvider userContext, string id)
    {
        // Integrate with ASP.NET Core's IAuthorizationService
        var authService = userContext.GetService();
        var user = userContext.GetService();
        // Custom policy: e.g., user must own the resource
        return authService.AuthorizeAsync(user, id, "OwnsResource").Result.Succeeded;
    }
}

Register this rule globally:

services.AddGraphQL(options =>
{
    options.ValidationRules = DocumentValidator.CoreRules
        .Concat(new[] { typeof(BatchAuthorizationValidationRule) })
        .ToArray();
});

3. Apply Per-Operation Rate Limiting Middleware
Use a distributed rate limiter (e.g., AspNetCoreRateLimit or custom middleware) that counts each operation in a batch separately. This middleware runs before GraphQL execution:

public class GraphQLBatchRateLimitMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IRateLimitService _rateLimitService;

    public GraphQLBatchRateLimitMiddleware(RequestDelegate next, IRateLimitService rateLimitService)
    {
        _next = next;
        _rateLimitService = rateLimitService;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        if (context.Request.Path.StartsWithSegments("/graphql") &&
            context.Request.Method == HttpMethods.Post)
        {
            var batch = await JsonSerializer.DeserializeAsync>(context.Request.Body);
            if (batch != null)
            {
                foreach (var operation in batch)
                {
                    var key = $"{context.User.Identity?.Name}:graphql:operation";
                    var result = await _rateLimitService.AcquireAsync(key, 1, TimeSpan.FromSeconds(1));
                    if (!result.IsAccepted)
                    {
                        context.Response.StatusCode = StatusCodes.Status429TooManyRequests;
                        await context.Response.WriteAsJsonAsync(new { error = "Rate limit exceeded per operation" });
                        return;
                    }
                }
            }
        }
        await _next(context);
    }
}

Apply this middleware in Startup.cs before GraphQL routing.

4. Enable Query Complexity Analysis
GraphQL.NET provides a QueryComplexityAnalyzer to reject overly expensive queries. Configure it with a max score per operation and apply it to each item in a batch:

services.AddGraphQL(options =>
{
    options.ExecutionOptions = new ExecutionOptions
    {
        ValidationRules = DocumentValidator.CoreRules
            .Concat(new[]
            {
                new QueryComplexityValidationRule(maxScore: 1000),
                new MaxDepthValidationRule(maxDepth: 10)
            })
            .ToArray()
    };
});

These measures ensure that batched requests in your ASP.NET GraphQL API are subject to the same security controls as individual requests, mitigating flooding, BOLA, and DoS risks.

Frequently Asked Questions

Why is GraphQL batching particularly risky in ASP.NET applications?
Batching in ASP.NET GraphQL (using GraphQL.NET) can bypass per-request rate limiting and authorization checks. A single HTTP request containing hundreds of operations may be counted as one request, allowing query flooding and mass data extraction (BOLA/IDOR). The default execution pipeline does not inherently validate each operation's cost or authorization context, making unconstrained batches a high-risk vector for data exposure and DoS.
How does middleBrick detect GraphQL batching vulnerabilities without credentials?
middleBrick sends a crafted batch of GraphQL queries (e.g., 50 operations requesting different resource IDs) to your ASP.NET endpoint. It then analyzes the responses: if all operations return valid data (e.g., user emails), it flags potential BOLA/IDOR. It also measures response times for batches of increasing size to detect missing rate limiting. The scan runs in 5–15 seconds and produces a risk score with specific findings and remediation code for GraphQL.NET.