Crlf Injection in Fiber with Hmac Signatures
Crlf Injection in Fiber with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Crlf Injection occurs when an attacker can inject a carriage return (CR, \r) and line feed (LF, \n) into a header or status line, causing the header to be split and additional headers or response content to be injected. In the Fiber web framework for Go, this can happen when user-controlled input (such as a request parameter, header, or cookie) is reflected into HTTP headers without proper sanitization. When Hmac Signatures are used—commonly to ensure integrity of a request or to sign query parameters, headers, or a body payload—the signature is typically computed over selected parts of the request. If the data used to compute the Hmac includes attacker-controlled header values or query parameters that contain \r\n, and those values are later reflected into headers, the signature may still validate while the injected characters alter the header structure.
Consider a scenario where a client sends a header X-Reference-ID that contains foo\r\nX-Internal: injected. The server computes an Hmac over the header value (or a concatenation of headers and possibly a timestamp) and includes that signature in another header, for example X-Request-Signature. Because the signature is tied to the original, unescaped content, the server may still accept it as valid. However, when the server reflects X-Reference-ID into an outbound header without sanitization, the injected \r\n causes the X-Internal: injected header to be interpreted as a separate header. This can lead to response splitting, header smuggling, or unauthorized control over cache directives or cookies. Even though Hmac Signatures protect integrity, they do not prevent the attacker from altering the semantic meaning of the message by injecting new headers if the application reflects untrusted data unchecked.
This becomes especially relevant when endpoints accept query parameters that influence headers—for example, a redirect endpoint that takes next and a token, signs the combination with Hmac, and then uses the signed result in a Location header. An attacker could supply next=https://example.com\r\nSet-Cookie: auth=evil; if the server signs the full string and later places it into a header without validation, the injected line can create a new header. Because the signature is computed over the attacker-controlled string, the server may still verify the signature successfully, mistakenly assuming that the content has not been altered. This illustrates why integrity checks like Hmac must be paired with strict input validation and canonicalization of any data that will be reflected into headers.
Hmac Signatures-Specific Remediation in Fiber — concrete code fixes
To prevent Crlf Injection when using Hmac Signatures in Fiber, you must ensure that any data used to compute or influence the signature is canonicalized and that any reflected values are strictly validated and encoded. Do not directly reflect user input into HTTP headers. Instead, maintain a server-side mapping (e.g., allowlist) of permitted values or use an indirect reference (ID) that maps to a server-controlled value. Always sanitize newlines and carriage returns before using data in headers, and enforce that header values do not contain \r or \n.
Below is a concrete example in Go using the Fiber framework and Hmac Signatures. The server expects a signature of selected headers, canonicalizes the input by rejecting control characters, and uses a strict allowlist for header names. It also returns a 400 error if dangerous characters are detected, preventing header injection.
// Example: Secure handling of headers and Hmac in Fiber
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"net/http"
"strings"
"github.com/gofiber/fiber/v2"
)
// canHeaderValue checks for Crlf injection patterns.
func canHeaderValue(value string) bool {
return !strings.ContainsAny(value, "\r\n")
}
// computeSignature creates an Hmac over selected headers in a canonical order.
func computeSignature(headers map[string]string, secret string) string {
mac := hmac.New(sha256.New, []byte(secret))
// Canonical order: use a fixed slice of keys to ensure deterministic input.
keys := []string{"X-Reference-ID", "X-Timestamp"}
for _, k := range keys {
if v, ok := headers[k]; ok {
mac.Write([]byte(v))
}
}
return hex.EncodeToString(mac.Sum(nil))
}
func main() {
app := fiber.New()
secret := "super-secret-key" // in practice, load from secure configuration
app.Post("/verify", func(c *fiber.Ctx) error {
refID := c.Get("X-Reference-ID", "")
timestamp := c.Get("X-Timestamp", "")
// Reject header values that could inject new headers.
if !canHeaderValue(refID) || !canHeaderValue(timestamp) {
return c.Status(fiber.StatusBadRequest).SendString("invalid header value")
}
headers := map[string]string{
"X-Reference-ID": refID,
"X-Timestamp": timestamp,
}
expectedSig := computeSignature(headers, secret)
providedSig := c.Get("X-Request-Signature", "")
if !hmac.Equal([]byte(expectedSig), []byte(providedSig)) {
return c.Status(fiber.StatusUnauthorized).SendString("invalid signature")
}
// Safe: use server-controlled values for redirects or further processing.
// Do not directly use refID in a Location header without strict validation.
return c.SendStatus(fiber.StatusOK)
})
app.Listen(":3000")
}
For scenarios where you need to pass dynamic values to clients (e.g., a signed redirect token), encode and scope the data server-side. Instead of reflecting the raw parameter into a header, store the intent server-side (e.g., in a session or a short-lived map keyed by a random ID), sign that key, and have a controlled endpoint resolve it. This approach ensures that Hmac Signatures protect integrity while eliminating the risk that reflected data introduces new headers via Crlf Injection.