Dns Cache Poisoning in Fiber
How DNS Cache Poisoning Manifests in Fiber
In Fiber applications, DNS cache poisoning typically emerges through Server-Side Request Forgery (SSRF) vulnerabilities, where an attacker manipulates a Fiber server into making requests to attacker-controlled domains. The core issue arises when Fiber's httpclient (used for outbound requests) or reverse proxy functionality resolves DNS without strict validation, allowing an attacker to poison the resolver's cache and redirect traffic.
Fiber-Specific Attack Pattern: DNS Rebinding via Unvalidated Proxying
Consider a Fiber endpoint that proxies user-supplied URLs:
package main
import (
"github.com/gofiber/fiber/v2"
"net/http"
)
func main() {
app := fiber.New()
// Vulnerable: proxies any URL without DNS validation
app.Get("/proxy", func(c *fiber.Ctx) error {
targetURL := c.Query("url") // e.g., ?url=http://internal-service/admin
resp, err := http.Get(targetURL) // Uses Go's default resolver
if err != nil {
return c.Status(500).SendString("Proxy error")
}
defer resp.Body.Close()
return c.Status(resp.StatusCode).SendStream(resp.Body)
})
app.Listen(":3000")
}An attacker can perform DNS rebinding: initially, attacker.com resolves to their server, passing the proxy check. Then, they poison the DNS cache (or exploit TTL expiration) so subsequent resolutions point to 127.0.0.1 (localhost) or an internal IP (e.g., 169.254.169.254 on AWS). The Fiber server, using the poisoned resolver, fetches internal metadata or services, bypassing network segmentation.
Fiber's httputil.ReverseProxy Misconfiguration
When Fiber applications use Go's httputil.ReverseProxy (common for API gateways), the Director function often rewrites the request URL without validating the host's DNS resolution:
app.Use(
func(c *fiber.Ctx) error {
proxy := &httputil.ReverseProxy{
Director: func(req *http.Request) {
// Vulnerable: req.Host may be attacker-controlled
req.URL.Scheme = "https"
req.URL.Host = c.Query("target") // No DNS pinning
req.Header.Set("X-Forwarded-Host", req.Host)
},
}
proxy.ServeHTTP(c.Context(), c.Request())
return nil
},
)If target is an attacker's domain that later resolves to an internal IP, the proxy forwards requests to the internal network. This is exacerbated by Go's default DNS resolver, which caches responses per-OS (not per-process), making cache poisoning possible at the host level if the attacker can influence DNS (e.g., via MITM on unencrypted DNS).
Fiber-Specific Detection
Detecting DNS cache poisoning risks in Fiber apps requires analyzing both runtime behavior and configuration. The vulnerability surfaces when Fiber's HTTP client or proxy uses DNS without pinning, allowing hostname-to-IP changes between request initiations. middleBrick's SSRF check actively probes for this by:
- Testing DNS rebinding resistance: middleBrick submits a URL that initially resolves to a controlled domain, then changes DNS to a loopback address (e.g.,
127.0.0.1) and rechecks if the Fiber app still fetches from the new IP. - Validating OpenAPI specs: If the Fiber app exposes an OpenAPI spec, middleBrick cross-references
urlortargetparameters with runtime behavior to identify unvalidated proxies. - Checking for CVE-2021-44228 (Log4Shell) patterns: While not Fiber-specific, many Fiber apps use Java dependencies; middleBrick scans for JNDI lookup triggers that could lead to DNS-based exfiltration.
Scanning with middleBrick
Use the CLI to scan a Fiber endpoint:
middlebrick scan https://api.example.com/proxy?url=http://attacker.commiddleBrick will:
- Send a probe where
attacker.cominitially resolves to middleBrick's server. - Change DNS for
attacker.comto127.0.0.1and resend the request. - If the Fiber app fetches from
127.0.0.1, it flags SSRF with high severity, noting DNS rebinding susceptibility. - It also checks for missing
Hostvalidation in reverse proxy setups, correlating with OWASP API Top 10: API1:2023 — Broken Object Level Authorization (BOLA) and API5:2023 — Broken Function Level Authorization (BFLA) when proxy targets are user-controlled.
Indicators in Reports
middleBrick's report will highlight:
- Category: SSRF
- Finding: "Endpoint accepts arbitrary URLs without DNS pinning, vulnerable to DNS rebinding."
- Evidence: HTTP
200 OKfrom127.0.0.1after DNS switch. - Remediation: "Implement DNS pinning via custom resolver in Fiber."
Fiber-Specific Remediation
Remediation in Fiber involves enforcing DNS pinning and validating proxy targets. Go's net.Resolver allows custom DNS resolution, which can be integrated with Fiber's HTTP client or reverse proxy.
1. Custom DNS Resolver with Pinning
Create a resolver that caches the initial IP and rejects subsequent resolutions that differ. This prevents rebinding:
package main
import (
"context"
"fmt"
"net"
"strings"
"github.com/gofiber/fiber/v2"
)
// pinnedResolver caches the first IP for a hostname
type pinnedResolver struct {
cache map[string]string
}
func (r *pinnedResolver) Resolve(ctx context.Context, network, name string) (net.Addr, error) {
// Only resolve once per hostname
if ip, ok := r.cache[name]; ok {
return &net.TCPAddr{IP: net.ParseIP(ip), Port: 80}, nil
}
// Use system DNS for first resolution
addrs, err := net.DefaultResolver.LookupIPAddr(ctx, network, name)
if err != nil || len(addrs) == 0 {
return nil, err
}
firstIP := addrs[0].IP.String()
r.cache[name] = firstIP
return &net.TCPAddr{IP: addrs[0].IP, Port: 80}, nil
}
func main() {
app := fiber.New()
resolver := &pinnedResolver{cache: make(map[string]string)}
// Custom HTTP client with pinned resolver
client := &http.Client{
Transport: &http.Transport{
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
// Extract hostname from address (e.g., "example.com:80")
host, _, _ := net.SplitHostPort(address)
// Use pinned resolver for the hostname
addr, err := resolver.Resolve(ctx, network, host)
if err != nil {
return nil, err
}
// Connect to the pinned IP
return net.Dial(network, addr.String())
},
},
}
app.Get("/safe-proxy", func(c *fiber.Ctx) error {
target := c.Query("url")
// Parse target to extract hostname
u, err := url.Parse(target)
if err != nil || (u.Scheme != "http" && u.Scheme != "https") {
return c.Status(400).SendString("Invalid URL")
}
// Validate hostname against allowlist (e.g., only external APIs)
if !allowedHost(u.Hostname()) {
return c.Status(403).SendString("Host not allowed")
}
resp, err := client.Get(target)
if err != nil {
return c.Status(500).SendString("Request failed")
}
defer resp.Body.Close()
return c.Status(resp.StatusCode).SendStream(resp.Body)
})
app.Listen(":3000")
}
func allowedHost(host string) bool {
// Example allowlist: only permit specific external domains
allowed := []string{"api.example.com", "trusted-service.com"}
for _, a := range allowed {
if strings.EqualFold(host, a) {
return true
}
}
return false
}2. Fiber Reverse Proxy with DNS Pinning
For reverse proxies, use httptrace to monitor DNS lookups and enforce pinning:
app.Use(
func(c *fiber.Ctx) error {
target := c.Query("target")
u, err := url.Parse(target)
if err != nil || !allowedHost(u.Hostname()) {
return c.Status(403).SendString("Invalid target")
}
req, _ := http.NewRequest("GET", target, nil)
trace := &httptrace.ClientTrace{
DNSStart: func(info *httptrace.DNSStartInfo) {
// Log or enforce DNS policy here
fmt.Printf("DNS lookup for: %s\n", info.Host)
},
DNSDone: func(info *httptrace.DNSDoneInfo) {
// Pin the first IP; reject if multiple IPs (potential load-balancer poisoning)
if len(info.Addrs) > 1 {
// Log warning; consider blocking
}
},
}
req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
resp, err := http.DefaultClient.Do(req)
if err != nil {
return c.Status(500).SendString("Proxy error")
}
defer resp.Body.Close()
return c.Status(resp.StatusCode).SendStream(resp.Body)
},
)3. Defense-in-Depth
Combine DNS pinning with:
- Strict allowlists for proxy targets (as shown).
- Network segmentation — run Fiber apps in a VPC with egress restrictions, so even if DNS is poisoned, internal IPs are unreachable.
- DNSSEC validation — use a resolver that validates DNSSEC (e.g.,
github.com/miekg/dnswithdns.Clientanddns.msgverification).
These Fiber-specific fixes ensure that even if the OS DNS cache is poisoned, the application's resolver uses a pinned IP, mitigating rebinding. Note: middleBrick detects the absence of such controls but does not implement them — it provides remediation guidance as shown.
FAQ
- Q: Does Fiber have built-in DNS pinning for proxies?
A: No. Fiber uses Go's standardhttpandhttputilpackages, which rely on the OS resolver without pinning. Developers must implement custom resolvers or use third-party libraries (likemiekg/dns) to enforce DNS pinning. - Q: How does middleBrick's SSRF check differ from generic scanners?
A: middleBrick actively performs DNS rebinding tests by changing DNS responses mid-scan, simulating real-world cache poisoning. It also correlates findings with OpenAPI specs to identify unvalidated proxy parameters, providing Fiber-specific remediation steps like implementing apinnedResolver.