Graphql Introspection in Echo Go with Api Keys
Graphql Introspection in Echo Go with Api Keys — 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 this capability is enabled in an Echo Go service and protected only by API keys, it can expose metadata that aids attackers in crafting further attacks. API keys are often passed in headers or query parameters and, if leaked or logged, grant broad access. Combining introspection with key-based access means an unauthenticated or low-privilege actor can retrieve the full schema, then identify privileged operations that rely on the same key-based authorization, leading to BOLA/IDOR or privilege escalation opportunities.
In Echo Go, developers commonly enable introspection to support client tooling, but they may not restrict it to authenticated or authorized traffic. Because API keys are static secrets embedded in requests, introspection responses do not enforce per-field authorization; they only confirm whether the key is valid. This creates a two-stage vulnerability: first, introspection reveals what operations exist; second, those operations can be exercised using the same key if coarse-grained checks are in place. Attack patterns such as schema mapping and sensitive field discovery align with OWASP API Top 10 categories like BOLA and Data Exposure, and findings from a middleBrick scan often highlight unrestricted introspection endpoints as a risk factor.
An example of an unsafe Echo Go handler that exposes introspection without additional constraints is one that forwards the GraphQL query directly to the resolver without verifying intent or scope. If an API key is compromised, an attacker can repeatedly call the introspection endpoint to map the API surface and then target high-value mutations. This is especially dangerous when the service also supports unauthenticated LLM endpoints, as schema details can be used to design prompt injection or data exfiltration strategies that bypass expected usage patterns. middleBrick’s LLM/AI Security checks can detect whether introspection responses or error messages leak schema details in model outputs, providing remediation guidance to limit exposure.
To contextualize the risk, consider a scenario where an Echo Go service uses a middleware that validates an API key but does not differentiate between read and write operations. Introspection remains open, and the schema includes queries for user profiles and administrative flags. An attacker with a valid key can run introspection, identify these queries, and then probe for BOLA by manipulating IDs in requests. A middleBrick scan in this environment would flag the combination of open introspection and key-only controls, surfacing findings with severity and specific remediation steps.
Real-world tooling support is essential. The middleBrick CLI allows you to scan from terminal with middlebrick scan
Api Keys-Specific Remediation in Echo Go — concrete code fixes
Remediation centers on limiting introspection to trusted contexts and tightening authorization around API key usage. Instead of allowing introspection globally, restrict it to authenticated sessions or administrative roles, and apply consistent checks across all GraphQL operations. In Echo Go, this can be achieved by wrapping the GraphQL handler with middleware that validates the key and inspects the request path or query content before permitting introspection queries.
Below are concrete code examples for secure handling in Echo Go. The first example shows a middleware that validates an API key and conditionally allows introspection:
// api_key_middleware.go
package main
import (
"net/http"
"strings"
"github.com/labstack/echo/v4"
)
func APIKeyMiddleware(validKeys map[string]bool) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
key := c.Request().Header.Get("X-API-Key")
if key == "" {
return echo.NewHTTPError(http.StatusUnauthorized, "missing api key")
}
if !validKeys[key] {
return echo.NewHTTPError(http.StatusForbidden, "invalid api key")
}
// Attach key identity for downstream use
c.Set("api_key", key)
return next(c)
}
}
}
// graphql_handler.go
package main
import (
"github.com/labstack/echo/v4"
"github.com/vektah/gqlparser/v2/gqlerror"
)
type GraphQLContext struct {
Schema *graphql.Schema
}
func (g *GraphQLContext) ServeHTTP(c echo.Context) error {
// Allow introspection only from trusted origins or methods
if isIntrospectionQuery(c.Request()) {
// Optionally require a special admin key or scope
if !isAuthorizedForIntrospection(c.Request()) {
return echo.NewHTTPError(http.StatusForbidden, "introspection not allowed")
}
}
var req struct {
Query string `json:"query"`
}
if err := c.Bind(&req); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "invalid request")
}
result := graphql.Do(graphql.Params{
Schema: g.Schema,
RequestString: req.Query,
})
if len(result.Errors) > 0 {
errs := make([]string, len(result.Errors))
for i, err := range result.Errors {
errs[i] = err.Error()
}
return c.JSON(http.StatusOK, map[string]interface{}{"errors": errs})
}
return c.JSON(http.StatusOK, result.Data)
}
func isIntrospectionQuery(r *http.Request) bool {
// Basic heuristic; refine based on your schema
const introspectionQuery = `__schema { queryType { name } }`
body := r.FormValue("query")
return strings.Contains(body, introspectionQuery)
}
func isAuthorizedForIntrospection(r *http.Request) bool {
key := r.Header.Get("X-API-Key")
// Example: allow only a specific admin key for introspection
return key == "admin-introspection-key"
}
The second example demonstrates how to integrate this into an Echo Go application with route-level protections, ensuring that public endpoints remain key-validated while introspection is further gated:
// main.go
package main
import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
func main() {
e := echo.New()
validKeys := map[string]bool{
"public-key-123": true,
"admin-introspection-key": true,
}
e.Use(middleware.Logger())
e.Use(APIKeyMiddleware(validKeys))
graphqlCtx := &GraphQLContext{
Schema: loadSchema(),
}
// Public route with key validation
e.POST("/graphql", graphqlCtx.ServeHTTP)
// Optionally separate introspection if needed
e.POST("/graphql/introspect", func(c echo.Context) error {
if !isAuthorizedForIntrospection(c.Request()) {
return echo.NewHTTPError(http.StatusForbidden, "introspection not allowed")
}
// similar handler but limited to schema queries
return graphqlCtx.ServeHTTP(c)
})
e.Logger.Fatal(e.Start(":8080"))
}
These examples ensure that API keys are validated before any GraphQL processing and that introspection is not freely exposed. By combining these patterns with continuous scanning via the middleBrick CLI or GitHub Action, you can detect regressions where introspection becomes unexpectedly permissive. The Pro plan’s continuous monitoring can alert you if the risk score changes due to configuration drift, and the dashboard helps track remediation progress over time.
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 |