HIGH graphql introspectionecho gobearer tokens

Graphql Introspection in Echo Go with Bearer Tokens

Graphql Introspection in Echo Go with Bearer Tokens — how this specific combination creates or exposes the vulnerability

GraphQL introspection is a feature that allows clients to query the schema of a GraphQL service, including types, queries, and mutations. When an Echo Go service exposes a GraphQL endpoint without restricting introspection, an unauthenticated or partially authenticated attacker can retrieve the full schema using an HTTP POST request with a standard introspection query. This becomes a security concern when the endpoint relies on Bearer Tokens for authorization but does not consistently enforce token validation for introspection operations.

In Echo Go, developers often configure routes with middleware that checks for Bearer Tokens in the Authorization header. However, if introspection is enabled and not guarded by the same strict middleware, an attacker can send an introspection request without a valid token or with a missing Authorization header and still learn the schema. The combination of open introspection and Bearer Tokens creates a discrepancy: some routes require tokens while introspection does not, effectively bypassing intended access controls. This can reveal sensitive field names, query patterns, and relationships that aid further attacks, such as IDOR or BOLA, especially when combined with other unchecked endpoints.

An example request to an unprotected introspection endpoint in Echo Go might look like an HTTP POST with a JSON body containing the introspection query. Even if Bearer Tokens protect production queries, lack of schema hiding in development or misconfigured middleware chains can leave introspection accessible. Attackers can exploit this without credentials, making it a useful reconnaissance step before attempting authenticated attacks like privilege escalation or property-level authorization bypass.

Bearer Tokens-Specific Remediation in Echo Go — concrete code fixes

To secure GraphQL introspection in Echo Go when using Bearer Tokens, ensure that introspection is either disabled in production or guarded by the same authentication and authorization checks applied to other GraphQL operations. Below are concrete remediation steps and code examples.

1. Disable introspection in production builds

Use environment variables to control introspection. In production, disable it unless explicitly needed for debugging.

import (
	"github.com/labstack/echo/v4"
	"github.com/samber/lo"
	"github.com/vektah/gqlparser/v2/gqlerror"
)

func newGraphQLServer(enableIntrospection bool) *echo.Group {
	g := echo.New().Group("/api")
	handler := func(c echo.Context) error {
		var req struct {
			Query string `json:"query"`
		}
		if err := c.Bind(&req); err != nil {
			return echo.NewHTTPError(400, "invalid request")
		}

		// Only allow introspection when explicitly enabled
		if req.Query == "" || (lo.Contains(req.Query, "__schema") || lo.Contains(req.Query, "__type")) && !enableIntrospection {
			return echo.NewHTTPError(403, "introspection disabled")
		}

		// Proceed with normal execution
		return c.JSON(map[string]interface{}{"data": "ok"})
	}

	g.POST("/graphql", echo.WrapHandlerFunc(handler))
	return g
}

2. Enforce Bearer Token validation before processing introspection queries

Ensure that the Bearer Token middleware runs for all GraphQL requests, including introspection. Validate the token and required scopes before allowing schema queries.

import (
	"net/http"
	"strings"

	"github.com/labstack/echo/v4"
)

func BearerAuthMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		auth := c.Request().Header.Get("Authorization")
		if auth == "" {
			return echo.NewHTTPError(http.StatusUnauthorized, "authorization header required")
		}

		const bearerPrefix = "Bearer "
		if !strings.HasPrefix(auth, bearerPrefix) {
			return echo.NewHTTPError(http.StatusUnauthorized, "invalid authorization header format")
		}

		token := strings.TrimPrefix(auth, bearerPrefix)
		if token == "" {
			return echo.NewHTTPError(http.StatusUnauthorized, "token cannot be empty")
		}

		// Here you would validate the token (e.g., verify JWT, check revocation)
		// For this example, we assume validation passes.

		return next(c)
	}
}

// Apply the middleware to the GraphQL route
func setupGraphQLRoute(g *echo.Group, enableIntrospection bool) {
	g.POST("/graphql", BearerAuthMiddleware(func(c echo.Context) error {
		var req struct {
			Query string `json:"query"`
		}
		if err := c.Bind(&req); err != nil {
			return echo.NewHTTPError(400, "invalid request")
		}

		if req.Query != "" && (strings.Contains(req.Query, "__schema") || strings.Contains(req.Query, "__type")) && !enableIntrospection {
			return echo.NewHTTPError(403, "introspection disabled")
		}

		// Normal GraphQL execution
		return c.JSON(map[string]interface{}{"data": "resolved"})
	}))
}

3. Use role-based access control for introspection

If introspection must remain enabled for certain clients, restrict it to specific roles or scopes encoded in the Bearer Token. Validate claims before allowing schema exposure.

func IntrospectionRoleMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		auth := c.Request().Header.Get("Authorization")
		if auth == "" {
			return echo.NewHTTPError(http.StatusUnauthorized, "authorization header required")
		}

		// Extract and validate token; assume claims extraction function exists
		claims, valid := validateTokenAndExtractClaims(auth) // pseudo-function
		if !valid {
			return echo.NewHTTPError(http.StatusUnauthorized, "invalid token")
		}

		// Allow introspection only for admin or specific roles
		if containsIntrospectionQuery(c) && !hasScope(claims, "graphql:Introspect") {
			return echo.NewHTTPError(403, "insufficient scope for introspection")
		}

		return next(c)
	}
}

By combining these practices—disabling introspection in production, enforcing Bearer Token validation for all GraphQL requests, and applying role-based checks—you reduce the risk of unintended schema exposure while maintaining secure API access.

Related CWEs: dataExposure

CWE IDNameSeverity
CWE-200Exposure of Sensitive Information HIGH
CWE-209Error Information Disclosure MEDIUM
CWE-213Exposure of Sensitive Information Due to Incompatible Policies HIGH
CWE-215Insertion of Sensitive Information Into Debugging Code MEDIUM
CWE-312Cleartext Storage of Sensitive Information HIGH
CWE-359Exposure of Private Personal Information (PII) HIGH
CWE-522Insufficiently Protected Credentials CRITICAL
CWE-532Insertion of Sensitive Information into Log File MEDIUM
CWE-538Insertion of Sensitive Information into Externally-Accessible File HIGH
CWE-540Inclusion of Sensitive Information in Source Code HIGH

Frequently Asked Questions

Can introspection be safely enabled in production if Bearer Tokens are required?
It is generally not recommended. Even with Bearer Tokens, introspection can expose detailed schema information that may aid attackers. Disable it in production unless strictly necessary and ensure tokens are validated for every introspection request.
How does middleBrick handle GraphQL introspection in scans?
middleBrick checks whether introspection queries are reachable without proper authentication. If the endpoint allows introspection without valid Bearer Tokens or exposes the schema publicly, it flags this as a finding with remediation guidance tied to authentication and schema exposure.