Ssrf Server Side in Echo Go with Hmac Signatures
Ssrf Server Side in Echo Go with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Server-side request forgery (SSRF) in an Echo Go service that uses HMAC signatures can occur when user-controlled data influences both the request destination and the signature verification logic. If your application accepts a URL or host header from the client and then reconstructs an HMAC-signed request without strict validation, an attacker can force the server to sign and forward requests to internal or unexpected endpoints.
Consider an endpoint that receives a URL path, a target host, and an HMAC signature. The server recomputes the HMAC over the path using a shared secret and compares it to the client-supplied signature. If the comparison is performed incorrectly (e.g., using a weak comparison), or if the target host is not strictly constrained, the server may be tricked into forwarding requests to internal metadata services (e.g., http://169.254.169.254 in cloud environments) while still producing a valid HMAC from the attacker-supplied data.
In Echo Go, this often arises when route parameters or query parameters are used to build a request without isolating the signing context from the destination. For example, if the HMAC is computed over a subset of the request that does not include the final target host, an attacker can change the host while the signature remains valid, enabling SSRF through a trusted signing mechanism.
Real-world patterns that increase risk include using HTTP client libraries that follow redirects by default, or allowing the client to specify headers that are included in the HMAC computation without strict allowlisting. In such cases, an attacker can supply a malicious internal IP or a cloud metadata URL, and the server will sign and forward the request, potentially leaking sensitive data or enabling further internal attacks.
The Echo Go framework does not inherently protect against SSRF; it relies on the developer to enforce destination allowlists, validate inputs, and ensure the HMAC scope covers all inputs that affect request routing. Without these safeguards, an endpoint designed to verify HMAC signatures can become a channel for SSRF because the signature provides a false sense of integrity while the underlying request target remains uncontrolled.
Hmac Signatures-Specific Remediation in Echo Go — concrete code fixes
To remediate SSRF in Echo Go when using HMAC signatures, strictly separate the signing scope from the destination and enforce allowlists for hosts and paths. The HMAC must cover the exact data that will be used to construct the outbound request, including any host or port that influences routing, and you must avoid reusing the same signature for multiple destinations.
Below is a concrete, secure example in Go using the Echo framework. It demonstrates host and path allowlisting, HMAC signing of the canonical request, and safe forwarding only to approved origins.
//go
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"net/http"
"strings"
"github.com/labstack/echo/v4"
)
var (
sharedSecret = []byte("your-256-bit-secret")
allowedHosts = map[string]bool{
"api.external.com": true,
"svc.internal.com": true,
}
// canonical paths that are safe to forward
allowedPaths = map[string]bool{
"/v1/resource": true,
"/v1/status": true,
}
)
func computeHMAC(payload string) string {
mac := hmac.New(sha256.New, sharedSecret)
mac.Write([]byte(payload))
return hex.EncodeToString(mac.Sum(nil))
}
func isAllowedHost(h string) bool {
return allowedHosts[strings.ToLower(h)]
}
func isAllowedPath(p string) bool {
return allowedPaths[p]
}
func secureHandler(c echo.Context) error {
// Example signed request data from client
reqHost := c.FormValue("host")
reqPath := c.FormValue("path")
reqMethod := c.FormValue("method")
reqBody := c.FormValue("body")
clientSig := c.FormValue("signature")
// Validate allowlist first
if !isAllowedHost(reqHost) || !isAllowedPath(reqPath) || reqMethod != "GET" {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "request not allowed"})
}
// Recompute HMAC over canonical request components
canonical := reqMethod + "|" + reqHost + "|" + reqPath + "|" + reqBody
expectedSig := computeHMAC(canonical)
if !hmac.Equal([]byte(expectedSig), []byte(clientSig)) {
return c.JSON(http.StatusForbidden, map[string]string{"error": "invalid signature"})
}
// Build and execute the outbound request safely
url := "https://" + reqHost + reqPath
req, err := http.NewRequest(reqMethod, url, nil)
if err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request"})
}
// Do not follow redirects automatically to prevent SSRF chaining
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
resp, err := client.Do(req)
if err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "request failed"})
}
defer resp.Body.Close()
// Return safe response metadata only
return c.JSON(http.StatusOK, map[string]interface{}{
"status": resp.StatusCode,
"host": reqHost,
"path": reqPath,
})
}
func main() {
e := echo.New()
e.POST("/forward", secureHandler)
e.Start(":8080")
}
Key remediation points:
- Host and path are validated against strict allowlists before any network call.
- The HMAC is computed over the method, host, path, and body together, ensuring that changing the host invalidates the signature.
- Redirects are disabled to prevent the server from being used as a proxy to internal destinations.
- The server does not trust client-supplied URLs for routing; it only uses them to select from preapproved targets.
If you use the middleBrick CLI to scan your Echo Go endpoints, you can validate that these controls are effective by running middlebrick scan <url> and reviewing the SSRF and input validation findings. For teams managing many services, the Pro plan’s continuous monitoring and GitHub Action integration can help ensure that new routes or signing logic do not reintroduce SSRF risks.