Dangling Dns in Gin with Bearer Tokens
Dangling Dns in Gin with Bearer Tokens — how this specific combination creates or exposes the vulnerability
A dangling DNS record occurs when a hostname (e.g., internal.corp.example) previously resolved to an internal or external service but now points to an unintended or unmanaged endpoint. In Gin, if route handlers validate Bearer tokens but then make outbound HTTP calls using a hostname derived from request data or configuration, the handler may inadvertently resolve the dangling name and send credentials or sensitive data to an attacker-controlled host.
Consider a Gin service that calls a downstream API using a base URL built from a subdomain supplied by the caller:
// WARNING: illustrative example only — not a recommended pattern
func HandleOrder(c *gin.Context) {
org := c.Param("org")
baseURL := "https://" + org + ".api.internal.example/health"
token := c.GetHeader("Authorization")
// token is a Bearer token intended for the downstream service
resp, err := http.Get(baseURL)
if err != nil { /* handle */ }
defer resp.Body.Close()
// process response, potentially including the token in logs or retries
}
If org is user-supplied and internal.example has a dangling DNS record (e.g., malicious.api.internal.example resolving to an attacker server), the Bearer token is transmitted to the attacker. The token may be accepted by the unintended host, leading to token replay or impersonation. Because Gin does not enforce hostname allowlists at the HTTP client level, this trust boundary is exposed. The vulnerability is not about token format or Gin middleware misconfiguration alone; it is about combining token-based authorization with dynamic host resolution that can point to uncontrolled infrastructure.
An attacker may control the dangling DNS entry or exploit a recently decommissioned host that still responds. Even with valid Bearer tokens, forwarding them to an unknown host violates the principle of zero trust. The Gin application may log the outbound request, inadvertently exposing the token in logs or metrics. In addition, if retries or redirects are followed without strict host validation, the token may be sent multiple times to the rogue endpoint. This pattern is especially risky when tokens have broad scopes or long lifetimes, as is common in service-to-service authentication where Bearer tokens are used for downstream API calls.
Compounding the issue, OpenAPI/Swagger analysis performed by middleBrick can detect mismatches between declared host expectations and observed runtime calls, highlighting where dynamic host construction occurs alongside authentication mechanisms. While middleBrick does not fix the code, its findings can guide developers to introduce strict hostname verification, avoid concatenating untrusted input into URLs, and validate that Bearer tokens are only sent to known, verified endpoints.
Bearer Tokens-Specific Remediation in Gin — concrete code fixes
Remediation focuses on preventing uncontrolled host resolution and ensuring Bearer tokens are only sent to explicitly trusted endpoints. Use a static allowlist of base URLs or a validated mapping from tenant/organization identifiers to predefined hosts, rather than constructing URLs from raw input.
Safe Gin handler example with Bearer token forwarding:
type HostMapper struct {
// OrgID -> validated base URL
hosts map[string]string
}
func NewHostMapper() *HostMapper {
return &HostResolver{
hosts: map[string]string{
"org-a": "https://api-a.example.com",
"org-b": "https://api-b.example.com",
},
}
}
func (m *HostMapper) Resolve(org string) (string, bool) {
url, ok := m.hosts[org]
return url, ok
}
func HandleOrder(mapper *HostMapper) gin.HandlerFunc {
return func(c *gin.Context) {
org := c.Param("org")
baseURL, ok := mapper.Resolve(org)
if !ok {
c.AbortWithStatusJSON(400, gin.H{"error": "invalid org"})
return
}
token := c.GetHeader("Authorization")
if token == "" {
c.AbortWithStatusJSON(401, gin.H{"error": "missing bearer token"})
return
}
// Ensure the token is intended for this audience; validate scopes as needed
client := &http.Client{}
req, _ := http.NewRequest("GET", baseURL+"/health", nil)
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Accept", "application/json")
resp, err := client.Do(req)
if err != nil { /* handle */ }
defer resp.Body.Close()
// Do not log the token; handle response safely
if resp.StatusCode != 200 {
c.AbortWithStatusJSON(resp.StatusCode, gin.H{"error": "downstream error"})
return
}
// process
}
}
Key remediation practices:
- Never concatenate user input directly into URLs used for authenticated requests.
- Use a pre-validated mapping or configuration to resolve service endpoints.
- Set timeouts and disable redirects or enforce strict hostname checks in the HTTP client if your runtime permits (configuration is outside scanner internals).
- Ensure Bearer tokens are scoped to the intended audience (e.g., via
audclaims or API-specific scopes) to reduce impact if a token is inadvertently sent to a rogue host due to other bugs. - Audit logs to ensure tokens are not recorded, and use structured logging that redacts authorization headers.
middleBrick’s CLI can be used in CI to scan the Gin endpoints and surface authentication and SSRF-related findings, while the GitHub Action can enforce a security score threshold before deployments that modify routing or authentication logic. The MCP Server allows developers to validate host resolution patterns directly within IDEs, reducing the chance of introducing dangling DNS dependencies.