Crlf Injection in Echo Go with Firestore
Crlf Injection in Echo Go with Firestore — how this specific combination creates or exposes the vulnerability
Crlf Injection occurs when user-controlled data is reflected into HTTP headers without proper sanitization, allowing an attacker to inject newline characters (CRLF = \r\n). In an Echo Go service that integrates with Google Firestore, this typically happens when response headers or redirect locations are built from Firestore document fields.
Consider an Echo Go handler that reads a document field such as display_name from Firestore and sets it in a custom header:
import (
"github.com/labstack/echo/v4"
"cloud.google.com/go/firestore"
)
func displayNameHandler(c echo.Context) error {
ctx := c.Request().Context()
docID := c.Param("id")
client, _ := firestore.NewClient(ctx, "my-project")
defer client.Close()
doc, err := client.Collection("users").Doc(docID).Get(ctx)
if err != nil {
return c.String(500, "error")
}
name := doc.Data()["display_name"].(string)
c.Response().Header().Set("X-Display-Name", name)
return c.NoContent(200)
}
If a document contains display_name: "Alice\r\nX-Injected: malicious", the header will be split, and the additional header X-Injected: malicious will be injected into the HTTP response. This can enable HTTP response splitting, cache poisoning, or cross-site scripting via injected headers.
The same risk exists when using Firestore fields in redirects:
url := doc.Data()["redirect_url"].(string)
return c.Redirect(302, url)
If the Firestore-stored URL contains https://example.com\r\nSet-Cookie: bad=1, the injected CRLF can create a new header, potentially leading to session fixation or phishing. Because Firestore stores user-controlled strings and Echo Go reflects them verbatim into headers, the combination exposes the application to Crlf Injection unless input is validated and sanitized.
Note that Firestore itself does not introduce CRLF Injection; the risk arises when Echo Go uses untrusted Firestore data in a context that interprets CRLF as a delimiter — typically HTTP headers or redirect URLs. This makes the integration point the critical attack surface.
Firestore-Specific Remediation in Echo Go — concrete code fixes
To remediate Crlf Injection in an Echo Go service using Firestore, ensure that any Firestore-derived data placed into HTTP headers or redirects is sanitized. The safest approach is to reject or transform strings that contain CRLF characters when they are used in header or redirect contexts.
1. Header sanitization: strip or replace CRLF characters before setting headers.
import "strings"
func sanitizeHeaderValue(value string) string {
// Remove carriage return and newline characters
value = strings.ReplaceAll(value, "\r", "")
value = strings.ReplaceAll(value, "\n", "")
return value
}
func displayNameHandler(c echo.Context) error {
ctx := c.Request().Context()
docID := c.Param("id")
client, _ := firestore.NewClient(ctx, "my-project")
defer client.Close()
doc, err := client.Collection("users").Doc(docID).Get(ctx)
if err != nil {
return c.String(500, "error")
}
name := doc.Data()["display_name"].(string)
safeName := sanitizeHeaderValue(name)
c.Response().Header().Set("X-Display-Name", safeName)
return c.NoContent(200)
}
2. Redirect URL validation: ensure the URL does not contain CRLF and is an expected format.
import (
"net/url"
"strings"
)
func redirectHandler(c echo.Context) error {
ctx := c.Request().Context()
docID := c.Param("id")
client, _ := firestore.NewClient(ctx, "my-project")
defer client.Close()
doc, err := client.Collection("links").Doc(docID).Get(ctx)
if err != nil {
return c.String(500, "error")
}
rawURL := doc.Data()["redirect_url"].(string)
if strings.ContainsAny(rawURL, "\r\n") {
return c.String(400, "invalid redirect URL")
}
parsed, err := url.Parse(rawURL)
if err != nil || parsed.Scheme == "" || parsed.Host == "" {
return c.String(400, "invalid URL")
}
return c.Redirect(302, rawURL)
}
3. For broader protection, consider a validation layer that applies to all Firestore fields used in headers, such as a helper that checks for newline and carriage return characters and returns an error if found.
func assertNoCrlf(value string, fieldName string) error {
if strings.ContainsRune(value, '\r') || strings.ContainsRune(value, '\n') {
return fmt.Errorf("%s must not contain CRLF characters", fieldName)
}
return nil
}
// Usage in handler:
if err := assertNoCrlf(name, "display_name"); err != nil {
return c.JSON(400, map[string]string{"error": err.Error()})
}
These Firestore-aware measures ensure that user-controlled data stored in Firestore cannot be used to inject CRLF sequences into HTTP responses when handled by Echo Go.