User-controlled URLs are risky. If your code fetches http://userInput, an attacker can provide http://localhost:6379 (probe internal Redis), http://169.254.169.254/metadata (steal AWS credentials), or http://192.168.1.1/admin (scan your network).
SaferClient blocks private IPs and localhost. It's defense-in-depth, not a security guarantee.
Use SaferClient for user-controlled URLs. Use regular http.Client for trusted URLs.
// ❌ Risky for user input
http.Get(userURL)
// ✅ Use this instead
client := httpclient.NewSaferClient(30 * time.Second)
client.Get(userURL)
Tested and verified:
localhost, *.localhostNot thoroughly tested:
IPv6 Policy: All IPv6 addresses are rejected until proper test coverage exists for edge cases like IPv4-mapped addresses (::ffff:127.0.0.1), zone IDs, and other IPv6-specific attack vectors. This is a conservative approach - legitimate IPv6 use cases will fail.
import "github.com/teranos/QNTX/internal/httpclient"
client := httpclient.NewSaferClient(30 * time.Second)
resp, err := client.Get(userURL)
if err != nil {
// Blocked or network error
return err
}
defer resp.Body.Close()
maxRedirects := 5
opts := httpclient.SaferClientOptions{
AllowedSchemes: []string{"https"},
MaxRedirects: &maxRedirects,
}
client := httpclient.NewSaferClientWithOptions(30*time.Second, opts)
// ⚠️ Tests only
allowPrivateIP := false
opts := httpclient.SaferClientOptions{
BlockPrivateIP: &allowPrivateIP,
}
client := httpclient.NewSaferClientWithOptions(30*time.Second, opts)
Why dial-time validation? Attackers can use DNS to bypass hostname-only filters. evil.com might resolve to 127.0.0.1. We check the actual IPs before connecting.
Limitation: Sophisticated DNS rebinding (where DNS changes between validation and connection) isn't actively tested. The dial-time check mitigates this but isn't foolproof.
go test -v github.com/teranos/QNTX/internal/httpclient
What's covered: RFC1918 ranges, loopback, AWS metadata IP, @ injection, scheme blocking, redirect chains
What's missing: IPv6 edge cases, DNS rebinding simulation, timing attacks
Use SaferClient for:
Use regular http.Client for:
Don't rely on SaferClient for:
If you need stronger protection:
The current implementation is better than http.Get(userURL), but it's not enterprise-grade SSRF protection.
internal/httpclient/safer_client.gointernal/httpclient/safer_client_test.go