HIGH crlf injectionfastapijwt tokens

Crlf Injection in Fastapi with Jwt Tokens

Crlf Injection in Fastapi with Jwt Tokens — how this specific combination creates or exposes the vulnerability

Crlf Injection occurs when user-controlled data is reflected in HTTP headers without sanitization, enabling an attacker to inject additional headers or break header lines using Carriage Return (\r) and Line Feed (\n) sequences. In Fastapi, this risk can intersect with JWT token handling when a developer inadvertently uses unchecked input to construct or influence response headers, for example via custom headers, location values after redirects, or when echoing claims from a decoded token into headers.

Consider an endpoint that receives a JWT and uses a claim such as username or sub in a header without validation:

from fastapi import FastAPI, Request, Header
from typing import Optional

app = FastAPI()

@app.get("/welcome")
def welcome(x_token: Optional[str] = Header(None)):
    user = {"name": "alice", "sub": "alice\r\nX-Injected: malicious"}
    return {"message": f"Hello {user['name']}", "header": x_token}

If the application uses values derived from JWT claims (e.g., sub or custom claims) to build response headers, and those values are reflected without sanitization, an attacker may inject newline sequences that cause the server to append arbitrary headers. While Fastapi does not automatically reflect JWT claims into headers, developers who copy claims into headers—such as X-User-Id or Location during redirects—create an opening for CRLF injection. This becomes particularly relevant when integrating with external services or logging, where unchecked token payloads might influence header construction downstream.

Another scenario involves redirects. If a JWT contains a URL or path claim used to redirect a client, unsanitized input can lead to response splitting:

from fastapi import FastAPI, HTTPException
from fastapi.responses import RedirectResponse

def build_redirect(target: str):
    # UNSAFE: target may come from a JWT claim
    return RedirectResponse(url=target)

@app.get("/go")
def go(url: str):
    return build_redirect(url)

An attacker supplying url as https://example.com\r\nSet-Cookie: session=evil can inject a new header if the value is used directly. Even when JWTs are verified and their payloads consumed, failing to validate or encode values that end up in headers reintroduces CRLF injection. The presence of JWTs does not cause the flaw, but it can amplify impact by providing a trusted-seeming source for malicious input that propagates into headers.

Middleware, dependencies, or libraries that log or mirror token data into headers also widen the attack surface. For example, echoing a jti (JWT ID) into a custom header without escaping newlines can allow header manipulation. Because Fastapi relies on underlying ASGI servers to finalize headers, unsanitized input reaching the response stage can corrupt header lines, enabling cache poisoning, session fixation, or HTTP response splitting.

Jwt Tokens-Specific Remediation in Fastapi — concrete code fixes

Remediation focuses on strict validation, input sanitization, and safe construction of headers and redirects. Never directly interpolate untrusted data—especially from JWT claims—into headers or redirect URLs. Use allowlists, canonicalization, and header-safe encoding.

1) Validate and sanitize values used in headers

Ensure any data derived from a JWT claim is stripped of CRLF sequences before being used in a header. For string values that must be reflected, remove or replace \r and \n:

import re
from fastapi import FastAPI, Header

app = FastAPI()

SAFE_HEADER_RE = re.compile(r"[^\x20-\x7e\x80-\xff]*")  # basic printable ASCII

def sanitize_header_value(value: str) -> str:
    # Remove CR and LF to prevent header injection
    return value.replace("\r", "").replace("\n", "")

@app.get("/profile")
def profile(x_token: str = Header(...)):
    # Assume payload includes a 'username' claim that is untrusted
    username = "alice\r\nX-Injected: malicious"
    safe_username = sanitize_header_value(username)
    return {"X-User": safe_username}

2) Use built-in redirect safety and avoid dynamic URLs from claims

When redirecting, prefer relative paths or validated absolute URLs. Reject or canonicalize URLs from JWT claims:

from urllib.parse import urlparse
from fastapi import FastAPI, HTTPException
from fastapi.responses import RedirectResponse

app = FastAPI()

def is_safe_url(url: str) -> bool:
    parsed = urlparse(url)
    # Allow only http/https and non-empty host
    return parsed.scheme in {"http", "https"} and parsed.netloc

@app.get("/redirect")
def redirect_to(target: str):
    if not is_safe_url(target):
        raise HTTPException(status_code=400, detail="Invalid redirect URL")
    # If target came from a JWT claim, validate thoroughly
    return RedirectResponse(url=target)

3) Encode or omit JWT-derived values in custom headers

If you must include a JWT claim in a header, base64-encode it or use a mapping to avoid newline interpretation:

import base64
from fastapi import FastAPI, Header

app = FastAPI()

@app.get("/data")
def data(token_sub: str = Header(...)):
    encoded = base64.b64encode(token_sub.encode("utf-8")).decode("ascii")
    return {"X-Sub-B64": encoded}

4) Centralize header construction and logging

Create a helper for header-safe outputs and ensure logging does not mirror unsanitized values into response headers. For example, when using middleware to log requests, sanitize any values taken from JWT claims:

def make_safe_header(value: str) -> str:
    return value.replace("\r", "").replace("\n", "").strip()

By validating JWT-derived inputs, disallowing newline characters in headers, and using safe redirects, you mitigate CRLF injection risks while still leveraging JWT claims in your Fastapi application.

Frequently Asked Questions

Can CRLF injection occur if JWTs are properly signed and verified?
Yes. Signature verification ensures authenticity and integrity of the token, but it does not prevent unsafe use of claims. If a verified JWT claim is reflected into headers or redirects without sanitization, CRLF injection remains possible.
Does Fastapi automatically protect against response splitting when using RedirectResponse?
Fastapi does not sanitize or validate redirect URLs by default. It is the developer’s responsibility to validate and canonicalize the target URL to prevent response splitting via CRLF injection.