Header Injection in Echo Go
How Header Injection Manifests in Echo Go
Header injection vulnerabilities in Echo Go typically arise when user-controlled input is reflected in HTTP response headers without proper validation or encoding. This can lead to HTTP response splitting, session fixation, or cross-site scripting (XSS) through the Set-Cookie header.
In Echo Go applications, header injection commonly occurs in these scenarios:
- Echo Go handlers that echo back request headers or parameters directly into response headers
- Middleware that constructs headers from user input without validation
- Custom authentication logic that builds
Set-Cookieheaders from user data - Proxy headers (
X-Forwarded-For,X-Real-IP) being reflected back to clients
Here's a vulnerable Echo Go code pattern:
func vulnerableHandler(c echo.Context) error {
// User-controlled input from query parameter
username := c.QueryParam("username")
// Direct header injection without validation
c.Response().Header().Set("X-User-Name", username)
return c.String(http.StatusOK, "OK")
}An attacker could exploit this by sending: ?username=normal\r\nSet-Cookie:sessionid=evil, which would inject a new cookie header and potentially hijack sessions.
Another common Echo Go pattern involves cookie manipulation:
func authHandler(c echo.Context) error {
userID := c.FormValue("user_id")
// Vulnerable: user input directly in Set-Cookie
c.SetCookie(&http.Cookie{
Name: "session_id",
Value: userID, // Attacker-controlled
Path: "/",
})
return c.String(http.StatusOK, "Authenticated")
}This allows attackers to inject arbitrary cookie values, potentially leading to session fixation attacks where they set a known session ID for another user.
Echo Go-Specific Detection
Detecting header injection in Echo Go applications requires both static analysis and runtime scanning. middleBrick's black-box scanner can identify these vulnerabilities by testing unauthenticated endpoints for header injection patterns.
For manual detection in Echo Go codebases, look for these patterns:
// Search for these dangerous patterns in your Echo Go handlers:
// 1. Direct header setting from user input
c.Response().Header().Set("Header-Name", userInput)
// 2. Cookie setting from unvalidated input
c.SetCookie(&http.Cookie{Value: userInput})
// 3. Header echoing
c.Response().Header().Set("Reflected-Header", c.Request().Header().Get("User-Header"))
middleBrick scans Echo Go applications by:
- Testing all endpoints with specially crafted payloads containing CRLF sequences
- Analyzing response headers for unexpected header injection
- Checking for reflected headers that could contain XSS payloads
- Verifying proper header encoding and validation
The scanner provides a security score (A-F) with specific findings like:
Header Injection - High Risk
- Location: /api/user/profile
- Issue: User ID reflected in X-User-ID header without validation
- Severity: High
- Remediation: Validate and encode user input before header injection
For Echo Go applications using middleware, ensure custom middleware doesn't introduce injection points:
func customHeaderMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Vulnerable: directly using request header
forwardedIP := c.Request().Header().Get("X-Forwarded-For")
c.Response().Header().Set("X-Client-IP", forwardedIP)
return next(c)
}
}
This middleware could be exploited if an attacker sends a crafted X-Forwarded-For header containing CRLF sequences.
Echo Go-Specific Remediation
Securing Echo Go applications against header injection requires input validation, proper encoding, and secure header construction patterns. Here are Echo Go-specific remediation techniques:
1. Input Validation and Sanitization:
import "github.com/go-playground/validator/v10"
var validate = validator.New()
func secureHandler(c echo.Context) error {
username := c.QueryParam("username")
// Validate input - only allow alphanumeric characters
if err := validate.Var(username, "required,alphanum,max=32"); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid username")
}
// Safe: validated input in header
c.Response().Header().Set("X-User-Name", username)
return c.String(http.StatusOK, "OK")
}
2. Safe Cookie Handling:
func secureAuthHandler(c echo.Context) error {
userID := c.FormValue("user_id")
// Validate and sanitize user ID
if err := validate.Var(userID, "required,alphanum,max=32"); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid user ID")
}
// Create secure cookie with proper attributes
cookie := &http.Cookie{
Name: "session_id",
Value: generateSecureSessionID(userID), // Server-generated, not user input
Path: "/",
HttpOnly: true,
Secure: true, // Only send over HTTPS
SameSite: http.SameSiteLaxMode,
}
c.SetCookie(cookie)
return c.String(http.StatusOK, "Authenticated")
}
3. Header Injection Prevention Middleware:
func headerInjectionProtection(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Check for dangerous headers in request
dangerousHeaders := []string{"X-Forwarded-For", "X-Real-IP"}
for _, header := range dangerousHeaders {
value := c.Request().Header().Get(header)
if containsCRLF(value) {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid header value")
}
}
return next(c)
}
}
func containsCRLF(input string) bool {
return strings.ContainsAny(input, "\r\n")
}
4. Using Echo Go's Built-in Security Features:
func main() {
e := echo.New()
// Echo Go's built-in secure middleware
e.Use(middleware.Secure())
// Custom header injection protection
e.Use(headerInjectionProtection)
// Rate limiting to prevent abuse
e.Use(middleware.RateLimiter(
middleware.NewRateLimiterMemoryStore(10, time.Minute),
))
e.GET("/api/user", secureHandler)
e.POST("/api/auth", secureAuthHandler)
e.Start(":8080")
}
5. Testing Your Remediation:
func TestHeaderInjection(t *testing.T) {
e := echo.New()
// Test handler with injection attempt
e.GET("/test", func(c echo.Context) error {
username := c.QueryParam("username")
// This should reject injection attempts
if err := validate.Var(username, "required,alphanum,max=32"); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid input")
}
c.Response().Header().Set("X-Test", username)
return c.String(http.StatusOK, "OK")
})
// Test injection attempt
req := httptest.NewRequest(http.MethodGet, "/test?username=normal%0d%0aSet-Cookie:evil", nil)
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
// Verify header injection was prevented
if strings.Contains(rec.Header().Get("Set-Cookie"), "evil") {
t.Error("Header injection vulnerability detected")
}
}