Graphql Introspection in Aspnet with Bearer Tokens
Graphql Introspection in Aspnet with Bearer Tokens — how this specific combination creates or exposes the vulnerability
GraphQL introspection in an ASP.NET API can expose the full schema, including query types, mutations, arguments, and nested relationships. When an endpoint is protected only by Bearer token authentication but does not restrict introspection, an unauthenticated or insufficiently scoped token may still allow attackers to probe the API’s structure. Because introspection queries are often enabled in development and sometimes left accessible in production, combining Bearer token mechanisms with permissive introspection settings creates a risk where attackers enumerate capabilities without needing elevated privileges.
In ASP.NET, GraphQL endpoints are typically implemented via middleware or controller actions that process POST or GET requests. If the server does not explicitly disable introspection or validate token scopes before resolving an introspection request, an attacker can send a standard introspection query such as { __schema { queryType { name } mutationType { name } subscriptionType { name } types { name } } } and receive a full schema in response. The presence of a Bearer token does not inherently prevent this if the token is accepted before authorization checks are applied, or if the introspection resolver does not inspect token scopes or roles.
This combination is particularly risky when tokens are issued with broad scopes or when token validation is decoupled from operation-level authorization. For example, an API might accept any valid token but fail to enforce that the token grants access to the introspection operation. Attackers can then leverage introspection to discover sensitive fields, query patterns, and potential data exposure paths, which can inform further attacks such as IDOR or BFLA. In regulated contexts, exposing schema details may inadvertently reveal data classifications or business logic that should remain hidden.
When using Bearer tokens, it is important to treat introspection as a privileged operation. Even if the token is valid, the server must explicitly decide whether introspection is allowed for that token’s scope or role. Without this check, the API effectively provides documentation to anyone who can reach the endpoint, undermining the security boundary that token-based authentication aims to enforce. This is why schema exposure must be evaluated alongside token handling, query parsing, and execution pipelines to ensure that exposure does not occur through commonly accepted authentication mechanisms.
middleBrick can detect whether an API exposes GraphQL introspection by testing unauthenticated and token-enabled endpoints and analyzing the OpenAPI specification alongside runtime behavior. By running the 12 security checks in parallel, including Authentication, Input Validation, and Property Authorization, it highlights findings such as excessive data exposure and missing operation-level controls, with prioritized remediation guidance mapped to frameworks like OWASP API Top 10.
Bearer Tokens-Specific Remediation in Aspnet — concrete code fixes
To secure GraphQL introspection in ASP.NET when using Bearer tokens, enforce explicit authorization checks before allowing introspection queries. This involves configuring the GraphQL endpoint to inspect token scopes or claims and deny introspection unless the token meets specific criteria. Below are concrete code examples demonstrating how to implement this in an ASP.NET Core application.
Example 1: Disabling Introspection Based on Token Scope
Configure the GraphQL server to reject introspection unless the token contains a required scope. This example uses GraphQL.Server with policy-based authorization.
// Program.cs or Startup.cs
using GraphQL.Server;
using GraphQL.Server.Ui.Playground;
using Microsoft.AspNetCore.Authentication.JwtBearer;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://your-identity-provider";
options.Audience = "your-api-audience";
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("IntrospectionAllowed", policy =>
policy.RequireClaim("scope", "introspection:query"));
});
builder.Services
.AddGraphQL(builtInTypesProvider: typeof(Query))
.AddGraphTypes(typeof(Schema).Assembly)
.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.UseGraphQL("/graphql", new GraphQLOptions { EnableMetrics = false });
app.UseGraphQLPlayground();
app.Run();
Example 2: Custom Introspection Middleware Check
Implement middleware that inspects the Bearer token before allowing introspection queries to execute. This ensures that even if introspection is enabled in the schema, it is gated by token validation logic.
// IntrospectionAuthorizationMiddleware.cs
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
public class IntrospectionAuthorizationMiddleware
{
private readonly RequestDelegate _next;
public IntrospectionAuthorizationMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var request = context.Request;
if (request.Path.StartsWithSegments("/graphql")
&& request.Method == "POST"
&& IsIntrospectionQuery(request)) // Implement parsing logic
{
if (!context.User.Identity.IsAuthenticated
|| !context.User.HasClaim(c => c.Type == "scope" && c.Value == "introspection:query"))
{
context.Response.StatusCode = 403;
await context.Response.WriteAsync("Introspection not allowed for this token.");
return;
}
}
await _next(context);
}
private bool IsIntrospectionQuery(HttpRequest request)
{
// Simplified check; in production, parse the JSON body or query string
using var reader = new StreamReader(request.Body);
var body = reader.ReadToEnd();
return body.Contains("__schema");
}
}
Example 3: Schema-Level Authorization Using User Context
Pass user context into the GraphQL resolver to conditionally allow introspection fields. This approach integrates with ASP.NET’s user system to enforce scope-based decisions at the field level.
// Schema.cs
public class Schema : GraphQL.Types.Schema
{
public Schema(IDependencyResolver resolver) : base(resolver)
{
Query = resolver.Resolve<Query>();
}
public override async Task<ExecutionResult> ExecuteAsync(ExecutionOptions options)
{
var user = options.UserContext as ClaimsPrincipal;
if (options.OperationName?.ToLower() == "introspection_query"
&& user != null
&& !user.HasClaim(c => c.Type == "scope" && c.Value == "introspection:query"))
{
options.CancellationToken.ThrowIfCancellationRequested();
throw new ExecutionError("Introspection is not permitted for this token.");
}
return await base.ExecuteAsync(options);
}
}
By combining these techniques, you ensure that Bearer token validation directly influences whether introspection is permitted. This reduces the risk of schema exposure and aligns with secure defaults. middleBrick’s checks for Authentication and Property Authorization help verify that such controls are correctly implemented and mapped to compliance requirements.
Related CWEs: dataExposure
| CWE ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |