---
title: Protect Against SSRF Attacks
impact: MEDIUM
impactDescription: prevents internal network access from user input
tags: ssrf, url, network, internal, security
---

## Protect Against SSRF Attacks

SSRF allows attackers to make requests from your server to internal services, local files, or cloud metadata endpoints.

**Incorrect (accepting user URLs without validation):**

```go
func Handler(w http.ResponseWriter, r *http.Request) {
    url := r.URL.Query().Get("url")
    resp, _ := http.Get(url) // Attacker controls URL!
    io.Copy(w, resp.Body)
}
// Attacker: ?url=http://169.254.169.254/latest/meta-data/
```

**Correct (URL validation and IP blocking):**

```go
import (
    "net"
    "net/url"
)

var allowedHosts = []string{"api.example.com", "cdn.example.com"}

func SafeFetch(userURL string) (*http.Response, error) {
    parsed, err := url.Parse(userURL)
    if err != nil {
        return nil, err
    }

    // 1. Protocol whitelist
    if parsed.Scheme != "http" && parsed.Scheme != "https" {
        return nil, errors.New("protocol not allowed")
    }

    // 2. Host whitelist
    isAllowed := false
    for _, h := range allowedHosts {
        if parsed.Hostname() == h {
            isAllowed = true
            break
        }
    }
    
    // 3. Resolve IP and block internal ranges
    ips, _ := net.LookupIP(parsed.Hostname())
    for _, ip := range ips {
        if ip.IsLoopback() || ip.IsPrivate() || ip.IsUnspecified() {
            return nil, errors.New("internal IP blocked")
        }
    }

    client := &http.Client{
        CheckRedirect: func(req *http.Request, via []*http.Request) error {
            return http.ErrUseLastResponse // Disable redirects
        },
    }
    return client.Get(userURL)
}
```

**Tools:** `net/url`, `net.LookupIP`, `gosec` (G107)
