Ssrf in Gin with Bearer Tokens
Ssrf in Gin with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Server-Side Request Forgery (SSRF) in Gin when Bearer Tokens are involved occurs when an attacker can coerce the server into making authenticated outbound HTTP requests to internal or restricted endpoints. In many APIs built with Gin, developers implement proxy-like routes that forward requests to downstream services and attach Authorization headers based on incoming request headers. If the upstream URL is derived from user input and the Authorization header is sourced from the request (e.g., via a forwarded Bearer token or a header copied from the client), the server can be made to perform authenticated requests to internal metadata services, cloud instance metadata APIs, or administrative interfaces.
For example, consider a Gin route that accepts a target URL and forwards a request while propagating a Bearer token extracted from the client’s Authorization header:
func ProxyHandler(c *gin.Context) {
target := c.Query("url")
token := c.GetHeader("Authorization")
if token == "" {
c.JSON(401, gin.H{"error": "missing authorization"})
return
}
req, _ := http.NewRequest("GET", target, nil)
req.Header.Set("Authorization", token)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
c.Data(resp.StatusCode, "application/octet-stream", body)
}
If an attacker can control the target parameter, they can set it to http://169.254.169.254/latest/meta-data/iam/security-credentials/ on an EC2 instance and use the forwarded Bearer token (which may be trusted internally) to access credentials. Even without cloud metadata, SSRF can bypass network ACLs because the server-side request originates from a trusted zone and carries a valid Bearer token. The presence of Bearer tokens does not cause SSRF by itself, but it amplifies impact: an SSRF that authenticates as a service account can read sensitive internal resources, call internal admin endpoints, or chain into other services that trust intra-service tokens.
In the context of middleBrick’s checks, this pattern would be flagged under SSRF and BFLA/Privilege Escalation: the scanner tests whether user-supplied URLs can trigger authenticated outbound requests and whether authorization headers derived from the request can be leveraged to escalate access. The scan also aligns with OWASP API Top 10 A05:2023 (Broken Function Level Authorization) when token propagation logic is coupled with insecure routing logic.
Bearer Tokens-Specific Remediation in Gin — concrete code fixes
Remediation focuses on removing uncontrolled Bearer token propagation and validating or rejecting user-supplied URLs that can lead to internal endpoints. Below are concrete, safe patterns for Gin handlers.
1. Do not forward caller-supplied Authorization headers
Strip or ignore incoming Authorization headers when constructing outbound requests. Use a fixed token stored securely (e.g., from environment variables) or use mutual TLS / service identity instead of forwarding client tokens.
func SafeProxyHandler(c *gin.Context) {
target := c.Query("url")
// Validate target against an allowlist or strict prefix
if !strings.HasPrefix(target, "https://api.example.com/") {
c.JSON(400, gin.H{"error": "invalid target"})
return
}
// Use a service-bound token, not the caller’s header
token := os.Getenv("SERVICE_TOKEN")
if token == "" {
c.JSON(500, gin.H{"error": "service token not configured"})
return
}
req, err := http.NewRequest("GET", target, nil)
if err != nil {
c.JSON(500, gin.H{"error": "invalid request"})
return
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
c.JSON(502, gin.H{"error": "upstream error"})
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
c.Data(resp.StatusCode, "application/json", body)
}
2. Strict URL allowlisting and no user-controlled redirects
Do not accept free-form URLs. Either hardcode endpoints or validate against a strict prefix and path pattern. Reject parameters that could resolve to internal IPs or cloud metadata addresses.
var allowedPrefix = "https://api.example.com/v1/"
func ValidateTarget(target string) bool {
u, err := url.Parse(target)
if err != nil {
return false
}
if u.Scheme != "https" {
return false
}
if !strings.HasPrefix(u.Host, "api.example.com") {
return false
}
if !strings.HasPrefix(u.Path, "/v1/") {
return false
}
return true
}
3. Use outbound network policies and service identity
While not code, ensure the Gin service runs with network policies that restrict egress to known endpoints and avoid binding service tokens to inbound request contexts. Prefer workload identity (e.g., Kubernetes service accounts) over static bearer tokens when possible.
These changes ensure that Bearer tokens are not reflected or proxied from client input, mitigating SSRR risks and reducing the blast radius of any remaining SSRF vectors.
Related CWEs: ssrf
| CWE ID | Name | Severity |
|---|---|---|
| CWE-918 | Server-Side Request Forgery (SSRF) | CRITICAL |
| CWE-441 | Unintended Proxy or Intermediary (Confused Deputy) | HIGH |