Ssrf Server Side in Fiber with Basic Auth
Ssrf Server Side in Fiber with Basic Auth — how this specific combination creates or exposes the vulnerability
Server-side request forgery (SSRF) in Go Fiber applications that use HTTP Basic Authentication creates a scenario where an attacker can leverage the server’s credentials to reach internal or restricted endpoints. When Basic Auth is handled naively—such as forwarding an incoming Authorization header directly to a downstream service without validation—an SSRF vulnerability can emerge because the server may act as a proxy with its own set of credentials.
Consider a vulnerable handler that accepts a URL from an unauthenticated or low-privilege caller and performs an HTTP request on behalf of the server. If the handler includes the original Basic Auth header when making the outbound call, the target server may trust those credentials and allow access to administrative or internal paths that would otherwise be unreachable. This is especially dangerous when the upstream service treats the Basic Auth credentials as a strong trust boundary, such as internal dashboards or metadata services that only expect requests from trusted components.
An SSRF chain can be compounded when the upstream service exposes an OpenAPI/Swagger specification with $ref resolution that describes internal endpoints. middleBrick’s OpenAPI/Swagger analysis can detect such references and surface them during scans, highlighting the risk of unintended internal connectivity. In parallel, active probes—such as attempting to reach metadata endpoints like http://169.254.169.254/latest/meta-data/ or internal Kubernetes services—can confirm whether the server will make requests to unexpected internal addresses when Basic Auth is supplied.
Because SSRF is about forcing the server to make arbitrary requests, the presence of Basic Auth does not mitigate the issue; it may actually increase the impact if the credentials are valid for sensitive internal services. Attack patterns include metadata exfiltration, internal service enumeration, and bypassing network-level segregation. Real-world examples align with CWE-918 and the OWASP API Top 10’s Excessive Data Exposure and Broken Object Level Authorization categories, where an API endpoint unintentionally grants access to backend systems.
middleBrick’s 12 security checks run in parallel and include SSRF testing alongside Authentication and Input Validation assessments. When scanning a Fiber endpoint that uses Basic Auth, the scanner validates whether outbound requests can be manipulated to reach internal destinations and whether credentials are reflected or reused in a way that violates the principle of least privilege.
Basic Auth-Specific Remediation in Fiber — concrete code fixes
To remediate SSRF when using Basic Auth in Fiber, avoid forwarding or reusing the incoming Authorization header when your server makes outbound HTTP requests. Instead, either strip the header or use a dedicated service identity with minimal privileges. Validate and constrain the target URL to a strict allowlist of permitted hosts and paths, and prefer using a configured API client that does not automatically propagate credentials.
Below are concrete, working examples for secure Fiber handlers. These snippets illustrate how to implement Basic Auth for incoming requests while ensuring outbound requests do not inadvertently expose internal resources or propagate credentials.
Example 1: Authenticate incoming requests without propagating credentials
package main
import (
"net/http"
"strings"
"github.com/gofiber/fiber/v2"
)
// validateBasicAuth checks a hardcoded user:pass for demonstration.
// In production, use a secure lookup (e.g., database, OAuth introspect).
func validateBasicAuth(c *fiber.Ctx) bool {
auth := c.Get(fiber.HeaderAuthorization)
if !strings.HasPrefix(auth, "Basic ") {
return false
}
// Decode and verify credentials securely; this is a simplified check.
return auth == "Basic dXNlcjpwYXNz" // user:pass in base64
}
// safeGet performs a request to a predefined service without forwarding incoming auth.
func safeGet(c *fiber.Ctx) error {
if !validateBasicAuth(c) {
return c.Status(fiber.StatusUnauthorized).SendString("Unauthorized")
}
client := &http.Client{}
req, err := http.NewRequest(http.MethodGet, "https://api.example.com/v1/public", nil)
if err != nil {
return c.Status(fiber.StatusInternalServerError).SendString("Invalid request")
}
// Explicitly do NOT set Authorization header from the client request.
req.Header.Set("X-Request-ID", c.Get(fiber.HeaderXRequestId))
resp, err := client.Do(req)
if err != nil || resp.StatusCode != http.StatusOK {
return c.Status(fiber.StatusBadGateway).SendString("Service unavailable")
}
defer resp.Body.Close()
return c.Status(fiber.StatusOK).SendString("OK")
}
func main() {
app := fiber.New()
app.Get("/data", safeGet)
app.Listen(":3000")
}
Example 2: Constrain target URLs and use a dedicated outbound credential
package main
import (
"fmt"
"net/http"
"net/url"
"strings"
"github.com/gofiber/fiber/v2"
)
// allowlist of permitted hostnames for outbound requests.
var allowedHosts = map[string]bool{
"api.example.com": true,
"internal.service": true,
}
func isAllowedHost(raw string) bool {
parsed, err := url.Parse(raw)
if err != nil {
return false
}
return allowedHosts[parsed.Hostname()]
}
// outboundClient returns a client configured with a fixed service identity.
func outboundClient() *http.Client {
return &http.Client{}
}
func makeRequest(c *fiber.Ctx) error {
var body struct {
URL string `json:"url"`
}
if err := c.BodyParser(&body); err != nil {
return c.Status(fiber.StatusBadRequest).SendString("Invalid body")
}
if !isAllowedHost(body.URL) {
return c.Status(fiber.StatusForbidden).SendString("Target not allowed")
}
req, err := http.NewRequest(http.MethodGet, body.URL, nil)
if err != nil {
return c.Status(fiber.StatusBadRequest).SendString("Invalid URL")
}
// Use a dedicated service account token, not the incoming auth.
req.Header.Set("Authorization", "Bearer service-token")
req.Header.Set("X-Request-ID", c.Get(fiber.HeaderXRequestId))
resp, err := outboundClient().Do(req)
if err != nil {
return c.Status(fiber.StatusInternalServerError).SendString("Request failed")
}
defer resp.Body.Close()
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"status": resp.Status,
"url": body.URL,
"message": "Request completed",
})
}
func main() {
app := fiber.New()
app.Post("/fetch", makeRequest)
fmt.Println("Server running on :3000")
app.Listen(":3000")
}
Key takeaways: never forward the incoming Basic Auth header to arbitrary destinations, validate and restrict target URLs, and use a fixed, least-privilege identity for outbound calls. These practices reduce the attack surface for SSRF and help ensure that authentication mechanisms do not inadvertently expand the server’s reach into sensitive internal resources.