Crlf Injection in Fastapi with Hmac Signatures
Crlf Injection in Fastapi with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Crlf Injection occurs when an attacker can inject carriage return (CR, \r) and line feed (\n) characters into a header or status-line value. In Fastapi, if user-controlled data is reflected in HTTP headers or the status line and those values are not strictly validated or encoded, an attacker can chain headers or split responses. This becomes particularly relevant when Hmac Signatures are used for request authentication and integrity: the signature is typically computed over selected headers and a method/path, and the server recomputes and compares it. If an attacker can inject a header via CRLF, they can smuggle an additional header that changes the set of signed headers or the request target seen by the server, leading to request smuggling, response splitting, or bypass of intended authorization checks.
For example, consider a Fastapi endpoint that uses a custom header X-Request-ID for idempotency and includes that header in the Hmac signature. An attacker could provide a value like abc\r\nX-Internal: override. If the server concatenates this value into the signature base without normalization, the injected header may be processed separately, potentially altering behavior or bypassing logic that depends on a single, expected header. In other scenarios, CRLF in a URL parameter that is later reflected in a Location header can cause open redirects or cache poisoning. Because Hmac Signatures bind specific headers and the request target, unchecked injection can undermine the integrity guarantees the signature is meant to provide.
Fastapi does not automatically sanitize header values; it relies on the underlying Starlette/ASGI server to parse messages. If the application directly uses request headers in signature computation or response construction without canonicalization and strict validation, the unauthenticated attack surface includes header manipulation. Tools such as middleBrick scan these unauthenticated surfaces and flag header injection risks among the 12 parallel security checks, highlighting findings tied to input validation and property authorization that can intersect with signature misuse.
Hmac Signatures-Specific Remediation in Fastapi — concrete code fixes
To mitigate Crlf Injection when using Hmac Signatures in Fastapi, ensure that any data used to compute or verify the signature is canonicalized and that header values are strictly validated and encoded. Do not directly concatenate raw user input into the signature base. Instead, normalize inputs, reject or encode CR/LF characters, and use a robust method for header selection and hashing.
Below is a concrete, working example of a Fastapi middleware that validates headers and computes Hmac signatures safely. It rejects header values containing CR or LF, selects a canonical set of headers for signing, and uses hashlib and hmac from the standard library.
from fastapi import Fastapi, Request, Header, HTTPException, Response
from fastapi.middleware import Middleware
from fastapi.middleware.base import BaseHTTPMiddleware
import hmac
import hashlib
import base64
import re
app = Fastapi()
# A simple in-memory secret; use a secure secret store in production
SECRET = b"super-secret-key"
# Canonical header names to include in the signature; keep this ordered
CANONICAL_HEADERS = ["x-request-id", "content-type"]
# Reject unsafe characters in header values
UNSAFE_HEADER_RE = re.compile(r"[\r\n]")
def validate_header_value(value: str) -> None:
if UNSAFE_HEADER_RE.search(value):
raise ValueError("Header value contains disallowed CR/LF characters")
class HmacSignatureMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
# Validate incoming headers used in signing
for header in CANONICAL_HEADERS:
value = request.headers.get(header, "")
if value and UNSAFE_HEADER_RE.search(value):
raise HTTPException(status_code=400, detail=f"Invalid header value for {header}")
# Compute signature over canonical headers
parts = []
for h in CANONICAL_HEADERS:
val = request.headers.get(h, "")
# Use a consistent format: lowercased header name and trimmed value
parts.append(f"{h.lower()}:{val.strip()}")
body = await request.body()
payload = "\n".join(parts).encode() + b"\n" + body
computed = hmac.new(SECRET, payload, hashlib.sha256).digest()
signature = base64.b64encode(computed).decode()
# Optionally verify an incoming signature (example: X-Signature header)
incoming = request.headers.get("X-Signature")
if incoming:
if not hmac.compare_digest(incoming, signature):
raise HTTPException(status_code=401, detail="Invalid signature")
response = await call_next(request)
# Example of safely setting a response header; ensure values are validated
response.headers["X-Request-Id"] = request.headers.get("X-Request-Id", "")
return response
app.add_middleware(HmacSignatureMiddleware)
@app.get("/items/{item_id}")
async def read_item(
item_id: int,
x_request_id: str = Header(None, alias="X-Request-ID")
):
return {"item_id": item_id, "x_request_id": x_request_id}
Key remediation steps in this example:
- Canonical header selection: only specific headers are included in the signature base, in a fixed order.
- Input validation: header values are checked for CR/LF before use, preventing response splitting or header injection.
- Consistent formatting: header names are normalized to lowercase and values are stripped to reduce variability.
- Secure comparison: signatures are compared using hmac.compare_digest to avoid timing attacks.
In production, rotate secrets, store them securely, and align the canonical header list with the actual API contract. middleBrick’s scans can highlight mismatches between documented headers and those included in the signature, as well as flag unsafe reflection patterns that could enable CRLF-based bypasses.