Crlf Injection in Echo Go
How Crlf Injection Manifests in Echo Go
CRLF injection in Echo Go occurs when unvalidated user input containing carriage return ( ) and line feed ( ) characters is incorporated into HTTP headers or response bodies. This vulnerability enables attackers to manipulate HTTP responses, inject malicious headers, or perform HTTP response splitting attacks.
In Echo Go applications, CRLF injection commonly appears in middleware that processes request headers, response writers, or template rendering systems. Consider this vulnerable Echo handler:
func vulnerableHandler(c echo.Context) error {
name := c.QueryParam("name")
// DANGEROUS: Direct interpolation without validation
return c.String(http.StatusOK, "Hello " + name)
}
If an attacker requests /?name=John%0D%0ASet-Cookie:%20malicious=true, the response becomes:
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Hello John
Set-Cookie: malicious=trueThis injects an arbitrary Set-Cookie header into the response. More sophisticated attacks can perform HTTP response splitting by injecting additional status lines:
name=John%0D%0A%0D%0AHTTP/1.1%20200%20OK%0D%0AContent-Type:%20text/html%0D%0A%0D%0A<script>alert('XSS')</script>Echo Go's middleware chain amplifies this risk. A vulnerable middleware might look like:
func vulnerableMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
userAgent := c.Request().Header.Get("User-Agent")
// DANGEROUS: Header value used without sanitization
c.Response().Header().Set("X-User-Agent", userAgent)
return next(c)
}
}
When combined with request smuggling or cache poisoning attacks, CRLF injection in Echo Go can lead to session fixation, cross-site scripting via header injection, or bypassing security controls that rely on specific header values.
Echo Go-Specific Detection
Detecting CRLF injection in Echo Go requires both static analysis and dynamic testing. Static analysis involves reviewing code for dangerous patterns where user input is incorporated into headers or responses without proper validation.
Manual code review should focus on these Echo Go patterns:
// Dangerous patterns to search for:
c.Response().Header().Set(key, value) // where value comes from user input
c.String() // string concatenation with query parameters
c.JSON() // struct fields populated from request data
middleware that copies request headers to response headers
Dynamic testing with middleBrick provides automated detection of CRLF injection vulnerabilities in Echo Go APIs. The scanner tests for:
- Header injection attempts using %0D%0A sequences
- Response splitting with crafted HTTP status lines
- Multi-line header injection in various Echo Go response methods
- Template rendering contexts where CRLF characters might be interpreted
- Middleware chain vulnerabilities across Echo Go's processing pipeline
middleBrick's scanning methodology specifically targets Echo Go's architecture:
$ middlebrick scan https://your-echo-api.com/api/v1/users
=== CRLF Injection Scan Results ===
✓ Header injection vulnerability detected in /api/v1/users (Severity: High)
✓ Response splitting possible in /api/v1/profile (Severity: Medium)
✓ Template context CRLF vulnerability in /api/v1/dashboard (Severity: Low)
Total Score: B (82/100)
Recommendations:
- Validate and sanitize all header values
- Use Echo's built-in header validation middleware
- Implement strict input validation for query parameters
The scanner's black-box approach tests the actual runtime behavior of your Echo Go application, identifying vulnerabilities that static analysis might miss, such as those introduced by third-party middleware or complex template rendering logic.
Echo Go-Specific Remediation
Echo Go provides several built-in mechanisms to prevent CRLF injection. The most effective approach combines input validation, output encoding, and Echo's built-in security features.
First, implement strict input validation using Echo's validator middleware:
package main
import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
func main() {
e := echo.New()
// Echo's built-in security middleware
e.Use(middleware.Secure())
// Custom CRLF validation middleware
e.Use(crlfProtectionMiddleware)
e.GET("/safe", safeHandler)
e.GET("/unsafe", vulnerableHandler)
e.Start(":8080")
}
// Comprehensive CRLF protection middleware
func crlfProtectionMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Validate request headers
for k, v := range c.Request().Header {
if containsCRLF(v[0]) {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid header value")
}
}
// Validate query parameters
if err := validateQueryParams(c); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid query parameter")
}
// Validate path parameters (if any)
if err := validatePathParams(c); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid path parameter")
}
return next(c)
}
}
func containsCRLF(input string) bool {
return strings.ContainsAny(input, "\r\n")
}
func validateQueryParams(c echo.Context) error {
for k, v := range c.QueryParams() {
if containsCRLF(v[0]) {
return fmt.Errorf("CRLF detected in parameter: %s", k)
}
}
return nil
}
func validatePathParams(c echo.Context) error {
for _, name := range c.ParamNames() {
value := c.Param(name)
if containsCRLF(value) {
return fmt.Errorf("CRLF detected in path parameter: %s", name)
}
}
return nil
}
// Safe handler with proper validation
func safeHandler(c echo.Context) error {
name := c.QueryParam("name")
// Validate input before use
if containsCRLF(name) {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid input")
}
return c.String(http.StatusOK, "Hello " + name)
}
// Alternative: Use context binding with validation
func validatedHandler(c echo.Context) error {
var input struct {
Name string `query:"name" validate:"required,alphanum,max=50"`
}
if err := c.Bind(&input); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid input")
}
if err := c.Validate(input); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid input")
}
return c.String(http.StatusOK, "Hello " + input.Name)
}
For template rendering, Echo Go's template engine automatically escapes content, but you should still validate inputs:
func templateHandler(c echo.Context) error {
name := c.QueryParam("name")
// Sanitize input for template context
safeName := sanitizeInput(name)
return c.Render(http.StatusOK, "greeting", map[string]string{
"Name": safeName,
})
}
func sanitizeInput(input string) string {
// Remove any CRLF characters
return strings.ReplaceAll(strings.ReplaceAll(input, "\r", ""), "\n", "")
}
Echo's built-in middleware provides additional protection:
e.Use(middleware.Secure({
Headers: map[string]string{
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
"X-XSS-Protection": "1; mode=block",
},
// Prevent header injection by validating header names
HeaderNameValidator: func(name string) bool {
// Disallow suspicious header names
return !strings.ContainsAny(name, "\r\n")
},
}))
Combine these approaches with regular middleBrick scanning to ensure your Echo Go application remains protected against CRLF injection attacks as your codebase evolves.