Ssrf Server Side in Chi with Mutual Tls
Ssrf Server Side in Chi with Mutual Tls — how this specific combination creates or exposes the vulnerability
Server-side SSRF occurs when an API endpoint accepts a URL from an attacker and uses it to make outbound HTTP requests. In Chi, this typically happens when a handler forwards user-supplied input to another service without strict validation. Mutual TLS (mTLS) is commonly used to authenticate and encrypt traffic between services. While mTLS strengthens service-to-service trust by requiring client certificates, it does not prevent an internal service from making unintended outbound calls. If a Chi application uses mTLS for its outbound client but does not validate or restrict the target hostname, an attacker can supply a malicious hostname that the internal service reaches via the mTLS connection. The presence of client certificates may even make the request succeed where plain HTTP would fail due to strict mTLS policies, increasing the risk of internal SSRF.
Chi routes are built using middleware and handlers. If a route accepts a URL parameter and passes it to an HTTP client configured with mTLS (client certificates and CA verification), the client may establish a TLS connection to the supplied host. Attackers can leverage this to probe internal endpoints, reach metadata services (e.g., cloud instance metadata), or bypass network segmentation that assumes mTLS confines traffic to trusted peers. Since mTLS ensures the client presents a valid certificate, the compromised service may be able to call internal APIs that reject untrusted clients, effectively extending the attack surface inward. This combination therefore exposes a class of SSRF where network-level protections are bypassed by leveraging trusted mTLS credentials.
Real-world examples include services that accept a webhook or target URL and perform an HTTP request using a pre-configured mTLS client. If the target hostname is not validated against an allowlist, an attacker can supply hostnames that resolve to internal services, cloud metadata endpoints (e.g., http://169.254.169.254), or other sensitive internal APIs. middleBrick can detect such unauthenticated SSRF patterns through its unauthenticated attack surface scans and its OWASP API Top 10 mappings, highlighting risky parameter handling and insufficient hostname validation even when mTLS is in use.
Mutual Tls-Specific Remediation in Chi — concrete code fixes
Remediation focuses on validating and restricting outbound targets, independent of the transport security. Never forward user input directly into an HTTP request. Instead, use an allowlist of permitted hostnames or domains, and enforce strict URL parsing to prevent hostname confusion (e.g., IPv4, IPv6, Unicode encodings). When using mTLS, ensure that hostname verification remains active and is not disabled through insecure TLS configurations.
Example: a Chi route that accepts a target host and path should resolve and validate the host against an allowlist before constructing the request. Use Go’s standard net/url and net/http packages alongside tls.Config that includes a proper root CAs and client certificates, while preserving default hostname verification.
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"net"
"net/http"
"net/url"
"strings"
)
// allowlist of permitted hosts
var allowedHosts = map[string]bool{
"api.example.com": true,
"service.internal": true,
}
func isValidHost(target string) bool {
u, err := url.Parse(target)
if err != nil {
return false
}
if u.Scheme != "https" {
return false
}
host := u.Hostname()
return allowedHosts[host]
}
func proxyHandler(w http.ResponseWriter, r *http.Request) {
target := r.URL.Query().Get("url")
if !isValidHost(target) {
http.Error(w, "invalid target host", http.StatusBadRequest)
return
}
parsed, _ := url.Parse(target)
// Load client cert and key
cert, err := tls.LoadX509KeyPair("client.crt", "client.key")
if err != nil {
http.Error(w, "server error", http.StatusInternalServerError)
return
}
// Load CA pool
caCert, err := io.ReadFile("ca.crt")
if err != nil {
http.Error(w, "server error", http.StatusInternalServerError)
return
}
caPool := x509.NewCertPool()
caPool.AppendCertsFromPEM(caCert)
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caPool,
InsecureSkipVerify: false, // ensure verification is enabled
ServerName: parsed.Hostname(),
}
transport := &http.Transport{
TLSClientConfig: tlsConfig,
DialTLS: func(network, addr string) (net.Conn, error) {
return tls.Dial(network, addr, tlsConfig)
},
}
client := &http.Client{Transport: transport}
resp, err := client.Get(target)
if err != nil {
http.Error(w, "upstream error", http.StatusBadGateway)
return
}
defer resp.Body.Close()
io.Copy(w, resp.Body)
}
Key points: keep InsecureSkipVerify false, set ServerName explicitly, and validate the hostname against an allowlist before using it in the request URL. Avoid disabling certificate validation or allowing wildcard hosts. middleBrick’s scans can highlight endpoints where user-controlled URLs are used without such validation, even when mTLS is configured.