Dns Rebinding in Fiber
How Dns Rebinding Manifests in Fiber
DNS rebinding attacks exploit the trust relationship between a web application and its backend services by manipulating DNS resolution to make the browser believe it's communicating with a trusted domain while actually connecting to a local or internal service.
In Fiber applications, this vulnerability often appears in API endpoints that accept URLs or hostnames from untrusted sources and then make HTTP requests to those destinations. The classic pattern involves an endpoint like:
app := fiber.New()
app.Post("/proxy", func(c *fiber.Ctx) error {
url := c.FormValue("url")
resp, err := http.Get(url)
if err != nil {
return c.Status(500).SendString(err.Error())
}
defer resp.Body.Close()
return c.SendString(resp.Status)
})
This proxy endpoint is vulnerable because it blindly follows any URL provided by the client. An attacker can host a malicious page that sets a short TTL DNS record for their domain, then quickly changes it to point to internal services like 192.168.1.1 or localhost. When the victim's browser accesses the malicious page, it makes requests to what it believes is the attacker's domain, but the requests actually reach internal services.
Fiber's default middleware stack doesn't provide any protection against this. The vulnerability becomes particularly dangerous when combined with:
- Database administration interfaces exposed on internal networks
- Development servers running on non-standard ports
- API endpoints that accept arbitrary URLs for processing
- Webhook receivers that make outbound HTTP requests
- File upload handlers that process remote files
A concrete Fiber-specific example might involve an endpoint that processes OpenAPI specifications:
app.Post("/upload-spec", func(c *fiber.Ctx) error {
file, err := c.FormFile("spec")
if err != nil {
return c.Status(400).SendString("No file uploaded")
}
// Vulnerable: processes remote files without validation
if strings.HasPrefix(file.Filename, "http") {
resp, err := http.Get(file.Filename)
if err != nil {
return c.Status(500).SendString(err.Error())
}
defer resp.Body.Close()
// Process the remote OpenAPI spec
spec, err := ioutil.ReadAll(resp.Body)
if err != nil {
return c.Status(500).SendString("Failed to read spec")
}
return c.JSON(analyzeSpec(spec))
}
return c.Status(400).SendString("Invalid file type")
})
This endpoint would allow an attacker to submit a URL pointing to internal services, potentially exposing sensitive data or triggering actions on internal systems.
Fiber-Specific Detection
Detecting DNS rebinding vulnerabilities in Fiber applications requires both static code analysis and dynamic testing. For static analysis, look for patterns where Fiber endpoints accept URLs or hostnames and make outbound requests without proper validation.
Using middleBrick's API scanning capabilities, you can identify these vulnerabilities without modifying your application code. middleBrick's black-box scanning approach tests the unauthenticated attack surface by:
- Analyzing endpoint parameters that accept URLs or hostnames
- Testing for SSRF-like behavior that could be exploited via DNS rebinding
- Checking for missing input validation on URL parameters
- Scanning for endpoints that make outbound HTTP requests
- Testing LLM/AI security endpoints that might be vulnerable to prompt injection via rebinding
The scanning process takes 5-15 seconds and provides a security risk score with specific findings. For Fiber applications, middleBrick would flag endpoints similar to the proxy example above and provide remediation guidance.
Manual detection in Fiber code involves searching for these patterns:
# Look for HTTP client usage with user-controlled URLs
grep -r "http\.Get\|http\.Post\|http\.Client" . | grep -E "(c\.FormValue|c\.Query|c\.Params)"
# Find file upload handlers that process URLs
grep -r "FormFile" . | grep -E "(http|https)"
# Check for webhook receivers
grep -r "webhook\|callback" . | grep -E "(http\.Get|http\.Post)"
During runtime testing, you can simulate basic DNS rebinding scenarios by:
app.Use(func(c *fiber.Ctx) error {
// Basic URL validation - prevent DNS rebinding
url := c.FormValue("url")
if url != "" {
u, err := url.Parse(url)
if err != nil {
return c.Status(400).SendString("Invalid URL")
}
// Block internal IP ranges
if isInternalIP(u.Hostname()) {
return c.Status(403).SendString("Internal URLs not allowed")
}
// Block private domains
if isPrivateDomain(u.Hostname()) {
return c.Status(403).SendString("Private domains not allowed")
}
}
return c.Next()
})
func isInternalIP(host string) bool {
ip := net.ParseIP(host)
if ip == nil {
return false
}
return ip.IsPrivate() || ip.IsLoopback() || ip.IsUnspecified()
}
func isPrivateDomain(host string) bool {
privateSuffixes := []string{".local", ".internal", ".corp", ".lan"}
for _, suffix := range privateSuffixes {
if strings.HasSuffix(host, suffix) {
return true
}
}
return false
}
middleBrick's continuous monitoring in the Pro plan would automatically re-scan your APIs on a schedule, alerting you if new endpoints are added that might be vulnerable to these attacks.
Fiber-Specific Remediation
Remediating DNS rebinding vulnerabilities in Fiber applications requires a defense-in-depth approach. The most effective strategy combines input validation, allowlisting, and safe HTTP client configurations.
First, implement strict URL validation using Go's net/url package combined with allowlisting:
func validateURL(rawURL string) (string, error) {
if rawURL == "" {
return "", errors.New("URL cannot be empty")
}
u, err := url.Parse(rawURL)
if err != nil {
return "", fmt.Errorf("invalid URL format: %w", err)
}
// Reject non-HTTP schemes
if u.Scheme != "http" && u.Scheme != "https" {
return "", errors.New("only HTTP/HTTPS URLs are allowed")
}
// Block internal IP addresses
host := u.Hostname()
if isInternalIP(host) {
return "", errors.New("internal IP addresses are not allowed")
}
// Allowlist specific domains
allowedDomains := map[string]bool{
"api.example.com": true,
"cdn.example.net": true,
}
if !allowedDomains[host] && !isPublicDomain(host) {
return "", errors.New("domain not in allowlist")
}
return u.String(), nil
}
func isInternalIP(host string) bool {
ip := net.ParseIP(host)
if ip == nil {
return false
}
return ip.IsPrivate() || ip.IsLoopback() || ip.IsUnspecified()
}
func isPublicDomain(host string) bool {
// Simple check - in production use a proper public suffix list
publicSuffixes := []string{".com", ".net", ".org", ".io"}
for _, suffix := range publicSuffixes {
if strings.HasSuffix(host, suffix) {
return true
}
}
return false
}
Apply this validation in your Fiber endpoints:
app.Post("/safe-proxy", func(c *fiber.Ctx) error {
rawURL := c.FormValue("url")
validatedURL, err := validateURL(rawURL)
if err != nil {
return c.Status(400).SendString(err.Error())
}
// Use a safe HTTP client with timeouts
client := &http.Client{
Timeout: 10 * time.Second,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
// Prevent redirect loops
if len(via) >= 5 {
return http.ErrUseLastResponse
}
return nil
},
}
resp, err := client.Get(validatedURL)
if err != nil {
return c.Status(502).SendString("Failed to fetch URL")
}
defer resp.Body.Close()
return c.SendString(resp.Status)
})
For webhook receivers and callback handlers, implement additional safeguards:
app.Post("/webhook", func(c *fiber.Ctx) error {
type webhookPayload struct {
CallbackURL string `json:"callback_url"`
Data string `json:"data"`
}
var payload webhookPayload
if err := c.BodyParser(&payload); err != nil {
return c.Status(400).SendString("Invalid payload")
}
// Validate callback URL
callbackURL, err := validateURL(payload.CallbackURL)
if err != nil {
return c.Status(400).SendString("Invalid callback URL")
}
// Process data
processed := processData(payload.Data)
// Make callback with safe client
client := &http.Client{
Timeout: 5 * time.Second,
Transport: &http.Transport{
DisableKeepAlives: true,
MaxIdleConns: 0,
},
}
callbackPayload := map[string]string{"result": processed}
resp, err := client.Post(callbackURL, "application/json",
strings.NewReader(jsonMarshal(callbackPayload)))
if err != nil {
return c.Status(502).SendString("Callback failed")
}
defer resp.Body.Close()
return c.JSON(fiber.Map{"status": "processed"})
})
For OpenAPI/Swagger processing endpoints, implement strict file handling:
app.Post("/upload-spec", func(c *fiber.Ctx) error {
file, err := c.FormFile("spec")
if err != nil {
return c.Status(400).SendString("No file uploaded")
}
// Only allow local file uploads, reject URLs
if strings.HasPrefix(file.Filename, "http") {
return c.Status(400).SendString("Remote files not allowed")
}
// Save to temporary location
tempPath := filepath.Join(os.TempDir(), file.Filename)
if err := c.SaveFile(file, tempPath); err != nil {
return c.Status(500).SendString("Failed to save file")
}
defer os.Remove(tempPath)
// Process the local file
specData, err := ioutil.ReadFile(tempPath)
if err != nil {
return c.Status(500).SendString("Failed to read spec")
}
return c.JSON(analyzeSpec(specData))
})
middleBrick's Pro plan includes continuous monitoring that would automatically detect if any of these patterns are introduced in new code, providing alerts before vulnerabilities reach production.