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 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 |