Ssrf in Echo Go
How SSRF Manifests in Echo Go
Server-Side Request Forgery (SSRF) in Echo Go typically occurs when the framework processes user-controlled URLs that get fetched internally. In Echo Go applications, SSRF vulnerabilities often appear in middleware that processes webhook callbacks, external API integrations, or proxy endpoints. The most common pattern involves using Go's http.Get() or http.Client with unsanitized user input.
func handleWebhook(c echo.Context) error {
url := c.QueryParam("callback_url")
resp, err := http.Get(url) // SSRF vulnerability
if err != nil {
return err
}
defer resp.Body.Close()
return c.JSON(http.StatusOK, echo.Map{"status": "processed"})
}
This code is vulnerable because an attacker can supply localhost, 127.0.0.1, or internal service URLs like http://metadata.google.internal/computeMetadata/v1/instance/. Echo Go's default HTTP client doesn't restrict outbound requests, making internal network reconnaissance trivial.
Another Echo Go-specific SSRF pattern appears in file upload handlers that process external URLs:
func uploadFromURL(c echo.Context) error {
url := c.FormValue("file_url")
resp, err := http.Get(url) // SSRF here
if err != nil {
return err
}
defer resp.Body.Close()
// Process file...
return c.JSON(http.StatusOK, echo.Map{"uploaded": true})
}
Echo Go's middleware ecosystem can also introduce SSRF risks. Custom middleware that fetches external resources based on request headers or query parameters creates attack surfaces. The framework's flexibility means SSRF can hide in unexpected places like custom validator implementations or business logic that fetches pricing data from external services.
Cloud-specific SSRF in Echo Go is particularly dangerous. Applications running on AWS, GCP, or Azure often have access to metadata services. An attacker might supply:
http://169.254.169.254/latest/meta-data/
http://metadata.google.internal/computeMetadata/v1/
http://169.254.169.254/latest/meta-data/iam/security-credentials/
These requests can expose cloud credentials, instance metadata, and service account tokens. Echo Go applications in containerized environments face additional risks from Docker daemon APIs (unix:///var/run/docker.sock) and Kubernetes service discovery.
Echo Go-Specific Detection
Detecting SSRF in Echo Go requires both static analysis and runtime scanning. Static analysis should focus on HTTP client usage patterns. Look for:
// Vulnerable patterns
go func() {
url := getUserInput()
http.Get(url) // UNSAFE
}
client := &http.Client{}
client.Get(unsafeURL) // UNSAFE
// Safe patterns
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
middleBrick's SSRF detection for Echo Go specifically identifies these patterns by scanning the runtime behavior of your API endpoints. It tests for SSRF by attempting to access restricted resources like:
http://127.0.0.1:80
http://localhost:22
http://169.254.169.254/latest/meta-data/
http://[::1]:80
The scanner analyzes Echo Go's routing structure to identify endpoints that accept URL parameters, then systematically tests each for SSRF vulnerabilities. It checks if the application properly validates hostnames, blocks private IP ranges, and restricts outbound requests.
For Echo Go applications, middleBrick provides specific findings like:
SSRF Vulnerability: /api/webhook?url=... (Critical)
- Allows outbound requests to internal networks
- No hostname validation or IP restriction
- Could access metadata services and internal APIs
Echo Go's middleware architecture means SSRF can be introduced through third-party middleware. middleBrick scans all registered middleware to identify potential SSRF vectors, including custom middleware that processes external URLs.
Runtime detection should include network monitoring for unexpected outbound requests. Echo Go applications should log all external requests with their originating endpoints. Tools like httplog middleware can track outbound connections:
func SSRFLoggingMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Log outbound requests
return next(c)
}
}
Echo Go-Specific Remediation
Remediating SSRF in Echo Go requires a defense-in-depth approach. The most effective solution is a URL allowlist combined with strict validation. Here's a comprehensive Echo Go SSRF prevention middleware:
type SSRFMiddleware struct {
allowedDomains map[string]bool
client *http.Client
}
func NewSSRFMiddleware(allowedDomains []string) *SSRFMiddleware {
domains := make(map[string]bool)
for _, domain := range allowedDomains {
domains[domain] = true
}
return &SSRFMiddleware{
allowedDomains: domains,
client: &http.Client{
Timeout: 10 * time.Second,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
},
}
}
func (m *SSRFMiddleware) ValidateURL(rawURL string) error {
parsed, err := url.Parse(rawURL)
if err != nil {
return errors.New("invalid URL format")
}
// Block private IP ranges
ip := net.ParseIP(parsed.Hostname())
if ip != nil {
if ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() {
return errors.New("private IP addresses not allowed")
}
}
// Block metadata services
if strings.Contains(parsed.Hostname(), "metadata") ||
strings.Contains(parsed.Hostname(), "169.254") ||
strings.Contains(parsed.Hostname(), "[::1]") {
return errors.New("metadata services blocked")
}
// Check allowlist
if !m.allowedDomains[parsed.Hostname()] {
return errors.New("domain not in allowlist")
}
return nil
}
func (m *SSRFMiddleware) SecureGet(rawURL string) (*http.Response, error) {
if err := m.ValidateURL(rawURL); err != nil {
return nil, err
}
return m.client.Get(rawURL)
}
Integrate this middleware into your Echo Go application:
allowed := []string{"api.example.com", "webhook.example.com"}
middleware := NewSSRFMiddleware(allowed)
func handleWebhook(c echo.Context) error {
url := c.QueryParam("callback_url")
resp, err := middleware.SecureGet(url)
if err != nil {
return c.JSON(http.StatusBadRequest, echo.Map{"error": err.Error()})
}
defer resp.Body.Close()
return c.JSON(http.StatusOK, echo.Map{"status": "processed"})
}
For Echo Go applications that must process arbitrary URLs, implement a proxy with strict controls:
func proxyRequest(c echo.Context) error {
targetURL := c.QueryParam("url")
// Validate using the middleware
if err := middleware.ValidateURL(targetURL); err != nil {
return c.JSON(http.StatusBadRequest, echo.Map{"error": err.Error()})
}
// Create request with timeout
req, err := http.NewRequest("GET", targetURL, nil)
if err != nil {
return err
}
client := &http.Client{
Timeout: 10 * time.Second,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
// Proxy the response
c.Response().Header().Set("Content-Type", resp.Header.Get("Content-Type"))
return c.Stream(resp.StatusCode, resp.Header.Get("Content-Type"), resp.Body)
}
Echo Go's context system can help track SSRF-related requests:
func SSRFContextMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
c.Set("ssrf_allowed", false)
// Check if this request should allow SSRF
// This could be based on auth, endpoint, or other criteria
return next(c)
}
}
Related CWEs: ssrf
| CWE ID | Name | Severity |
|---|---|---|
| CWE-918 | Server-Side Request Forgery (SSRF) | CRITICAL |
| CWE-441 | Unintended Proxy or Intermediary (Confused Deputy) | HIGH |