Ssrf Server Side in Chi
How SSRF Manifests in Chi
Server-Side Request Forgery (SSRF) in Chi manifests through HTTP handler functions that make outbound requests based on untrusted user input. Chi's router pattern, where handlers receive context and request objects, creates specific SSRF vulnerabilities when those handlers construct URLs for internal API calls, database queries, or external service integrations.
The most common SSRF pattern in Chi applications occurs when handlers accept URLs as parameters and use them to make HTTP requests. For example:
func proxyHandler(w http.ResponseWriter, r *http.Request) {
targetURL := r.URL.Query().Get("url")
resp, err := http.Get(targetURL) // SSRF vulnerability!
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer resp.Body.Close()
io.Copy(w, resp.Body)
}This pattern is particularly dangerous in Chi applications because the framework's middleware chain can mask where the vulnerability originates. A middleware might extract a URL from headers, cookies, or request bodies, then pass it to handlers that make the actual request.
Chi-specific SSRF scenarios include:
- Service discovery endpoints that query internal microservices using URLs from request parameters
- Webhook handlers that call back to URLs provided in webhook payloads
- API gateway patterns where Chi routes forward requests to backend services based on path parameters
- Configuration endpoints that read from URLs pointing to internal configuration files
The severity escalates when SSRF allows access to internal services on private networks. An attacker might exploit this to reach services on 127.0.0.1, 10.x.x.x, 172.16.x.x, or 192.168.x.x networks that aren't exposed to the internet. This can lead to database enumeration, internal API abuse, or cloud metadata service access.
Chi-Specific Detection
Detecting SSRF in Chi applications requires both static analysis and runtime scanning. Static analysis should focus on handler functions that construct HTTP requests using request-derived data. Look for patterns where URL strings are built from query parameters, headers, or JSON bodies.
middleBrick's SSRF detection for Chi applications includes:
- URL parameter extraction from request objects
- Outbound HTTP request construction patterns
- Network access attempts to private IP ranges
- Cloud metadata service endpoint probing (169.254.169.254, 169.254.170.2)
- SSRF-specific payloads testing for blind SSRF conditions
Using middleBrick's CLI for Chi applications:
npx middlebrick scan http://your-chi-app.com/api/proxy
The scanner tests common SSRF vectors including:
http://127.0.0.1:3306 (MySQL)
http://169.254.169.254/latest/meta-data/ (AWS metadata)
http://169.254.170.2/ (ECS metadata)
http://metadata.google.internal/computeMetadata/v1/ (GCP metadata)
middleBrick's runtime scanning also checks for SSRF in OpenAPI specifications. If your Chi application serves an OpenAPI spec, middleBrick cross-references endpoint definitions with actual runtime behavior to identify discrepancies that might indicate SSRF vulnerabilities.
Manual detection should include reviewing all Chi route handlers for outbound HTTP calls. Pay special attention to handlers that:
- Proxy requests to other services
- Fetch external resources
- Call webhook URLs
- Access configuration files via URLs
Chi-Specific Remediation
Remediating SSRF in Chi applications requires a defense-in-depth approach. The most effective strategy combines input validation, allowlisting, and network-level controls.
Input validation using Go's standard library:
import (
"net/url"
"regexp"
)
var validURLPattern = regexp.MustCompile(`^https?://[a-zA-Z0-9.-]+(:[0-9]+)?(/.*)?$`)
func validateURL(rawURL string) (string, error) {
if !validURLPattern.MatchString(rawURL) {
return "", errors.New("invalid URL format")
}
parsed, err := url.Parse(rawURL)
if err != nil {
return "", err
}
// Block private IP ranges
if isPrivateIP(parsed.Hostname()) {
return "", errors.New("private IP addresses not allowed")
}
// Block cloud metadata services
if isMetadataService(parsed.Hostname()) {
return "", errors.New("metadata service access denied")
}
return parsed.String(), nil
}
func isPrivateIP(host string) bool {
privateBlocks := []*net.IPNet{
{IP: net.ParseIP("10.0.0.0"), Mask: net.CIDRMask(8, 32)},
{IP: net.ParseIP("172.16.0.0"), Mask: net.CIDRMask(12, 32)},
{IP: net.ParseIP("192.168.0.0"), Mask: net.CIDRMask(16, 32)},
{IP: net.ParseIP("127.0.0.0"), Mask: net.CIDRMask(8, 32)},
}
ip := net.ParseIP(host)
if ip == nil {
return false
}
for _, block := range privateBlocks {
if block.Contains(ip) {
return true
}
}
return false
}
func isMetadataService(host string) bool {
metadataHosts := map[string]bool{
"169.254.169.254": true,
"169.254.170.2": true,
"metadata.google.internal": true,
"169.254.169.254": true,
}
return metadataHosts[host]
}
Using Chi's middleware for SSRF protection:
func ssrfProtection(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Check for SSRF indicators in request
if containsSSRFIndicator(r) {
http.Error(w, "SSRF attempt detected", http.StatusBadRequest)
return
}
next.ServeHTTP(w, r)
})
}
func containsSSRFIndicator(r *http.Request) bool {
// Check query parameters
q := r.URL.Query()
for _, v := range q {
if isSuspiciousURL(v[0]) {
return true
}
}
// Check headers
for _, values := range r.Header {
for _, v := range values {
if isSuspiciousURL(v) {
return true
}
}
}
return false
}
func isSuspiciousURL(input string) bool {
// Check for common SSRF patterns
patterns := []string{
"127.0.0.1",
"localhost",
"169.254",
"metadata",
"\.internal",
}
for _, pattern := range patterns {
if strings.Contains(input, pattern) {
return true
}
}
return false
}
Network-level SSRF mitigation using Go's HTTP client with custom transport:
type restrictedTransport struct {
http.RoundTripper
}
func (t *restrictedTransport) RoundTrip(req *http.Request) (*http.Response, error) {
host, _, err := net.SplitHostPort(req.URL.Host)
if err != nil {
host = req.URL.Host
}
if isPrivateIP(host) || isMetadataService(host) {
return nil, errors.New("blocked host: private IP or metadata service")
}
return t.RoundTripper.RoundTrip(req)
}
func NewSafeHTTPClient() *http.Client {
return &http.Client{
Transport: &restrictedTransport{
http.DefaultTransport,
},
Timeout: 10 * time.Second,
}
}
Integrating SSRF protection into Chi applications using middleBrick's CI/CD integration ensures continuous monitoring:
- name: Run SSRF security scan
run: |
npx middlebrick scan http://staging.your-chi-app.com \
--fail-below B \
--output json > ssrf-report.json
if [ $? -ne 0 ]; then
echo "SSRF vulnerabilities detected!"
exit 1
fi
Frequently Asked Questions
How can I test if my Chi application has SSRF vulnerabilities?
npx middlebrick scan <your-chi-app-url>. The scanner tests for SSRF by attempting requests to private IP ranges, cloud metadata services, and other SSRF indicators. It also analyzes your OpenAPI spec if available and checks for SSRF patterns in your runtime behavior.