Ssrf Server Side in Gin with Basic Auth
Ssrf Server Side in Gin with Basic Auth — how this specific combination creates or exposes the vulnerability
Server Side Request Forgery (SSRF) in a Gin service that uses HTTP Basic Authentication can expose internal resources and bypass intended network boundaries. When endpoints accept user-supplied URLs and make requests on the server, an attacker can supply a malicious target that traverses to internal services, metadata endpoints, or restricted administrative interfaces. The presence of Basic Auth on the Gin routes does not prevent SSRF; it only protects the endpoint that receives the client request. The server-side request initiated by Gin may lack appropriate credentials when reaching internal services, or it may forward credentials in an unsafe way if developers mistakenly propagate the client-supplied Authorization header.
For example, consider a Gin handler that accepts a URL parameter to fetch external data. If the handler uses Go’s standard library without validating the host, an attacker can provide a URL pointing to http://169.254.169.254/latest/meta-data/ (AWS instance metadata) or to internal Kubernetes services such as http://kubernetes.default.svc. Basic Auth protecting the Gin route does not mitigate this, because the SSRF occurs on the server side as the Gin service makes the outbound request. In some configurations, if the Gin server is deployed inside a trusted network, the attacker can leverage SSRF to probe internal IPs, reach databases on localhost, or interact with cloud metadata services that are not exposed publicly.
OpenAPI/Swagger specifications that define parameters as URLs without constraining schemes or hosts can exacerbate the issue. When combined with runtime SSRF testing in black-box scans, middleBrick checks whether user-supplied URLs can reach internal endpoints, regardless of whether the Gin route uses Basic Auth. This highlights the importance of validating and sanitizing user-controlled URLs and ensuring outbound requests do not inadvertently forward sensitive authentication material or trust internal network assumptions.
Basic Auth-Specific Remediation in Gin — concrete code fixes
To secure a Gin service with Basic Auth while mitigating SSRF risks, apply strict input validation and avoid propagating client credentials to outbound requests. Use explicit allowlists for hosts and schemes, and do not forward the incoming Authorization header to downstream services unless explicitly required and carefully scoped.
Basic Auth implementation in Gin (correct pattern)
func main() {
router := gin.Default()
// Basic Auth middleware for the whole router or specific routes
router.Use(basicAuth())
// A controlled endpoint that does NOT forward the Authorization header
router.GET("/external-data", func(c *gin.Context) {
// Validate and restrict the target host
target := c.Query("url")
if !isValidExternalURL(target) {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "invalid or disallowed URL"})
return
}
// Create request without inheriting client credentials
req, err := http.NewRequestWithContext(c.Request.Context(), "GET", target, nil)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
return
}
// Explicitly set safe headers; do not forward Authorization from the client
req.Header.Set("User-Agent", "middleBrick-integration/1.0")
client := &http.Client{
Timeout: 10 * time.Second,
}
resp, err := client.Do(req)
if err != nil {
c.AbortWithStatusJSON(http.StatusServiceUnavailable, gin.H{"error": "failed to fetch"})
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
c.Data(resp.StatusCode, "application/json", body)
})
router.Run(":8080")
}
// Basic Auth middleware using a fixed username/password or a validation function
func basicAuth() gin.HandlerFunc {
return func(c *gin.Context) {
user, pass, ok := c.Request.BasicAuth()
if !ok || !validateUserPass(user, pass) {
c.Header("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`)
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "auth required"})
return
}
c.Next()
}
}
func validateUserPass(user, pass string) bool {
// Use constant-time comparison and secure credential storage in production
return user == "apiuser" && pass == "S3cur3P@ss!"
}
// Strict URL validation: allowlist schemes and hosts
func isValidExternalURL(raw string) bool {
u, err := url.Parse(raw)
if err != nil || u.Scheme == "" || u.Host == "" {
return false
}
allowedSchemes := map[string]bool{"https": true}
allowedHosts := map[string]bool{"api.example.com": true, "data.example.org": true}
return allowedSchemes[u.Scheme] && allowedHosts[u.Host]
}
Key remediation points
- Do not forward the client-supplied Authorization header to the target URL. This prevents leaking credentials and reduces the risk of unintended authentication to internal services.
- Enforce an allowlist for URL schemes (use
httpsonly) and hosts. Reject URLs pointing to private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), loopback, and cloud metadata endpoints (169.254.169.254). - Use a dedicated outbound HTTP client with timeouts and avoid inheriting transport configurations that might trust internal proxies or environment variables.
- Combine Basic Auth at the Gin layer with network-level protections (firewalls, service mesh policies) to limit which internal endpoints are reachable from the Gin service.