Crlf Injection in Actix with Jwt Tokens
Crlf Injection in Actix with Jwt Tokens — 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 cookie value, causing the header to be split and additional headers or response-splitting based content to be injected. In Actix web applications, this risk is heightened when JWT tokens are handled in ways that reflect user input into HTTP headers or cookies without strict validation or sanitization.
Consider an Actix service that accepts a JWT token via a query parameter or a custom header, decodes it for introspection, and then uses a claim (such as a username or a session identifier) to construct a Set-Cookie or a Location header. If the claim is not sanitized, an attacker can supply a token with a payload like username=attacker%0d%0aSet-Cookie:%20auth=evil. When Actix uses this value to build a response header, the injected CRLF sequence can cause the response to be split, smuggling a new header or cookie into the response. This can lead to HTTP response splitting, cache poisoning, or client-side injection, depending on how the downstream server or browser handles the smuggled content.
Another scenario involves redirect flows where the application builds a redirect URL using a JWT token or a claim from it (e.g., redirect_to={token_payload}) without validation. If the token contains CRLF sequences and the framework directly concatenates the value into a Location header, the injected CRLF can terminate the header block and inject a new response, potentially smuggling an attacker-controlled URL to the client. Since JWT tokens are often treated as opaque or semi-trusted, developers may skip input validation, inadvertently exposing the attack surface.
Even when JWT tokens are validated cryptographically, the claims extracted from them can still be reflected in headers if the application logic is not careful. For example, using a user role from a verified token to set a custom header like X-User-Role: {role} without sanitizing CR/LF enables injection. Actix does not automatically sanitize header values; it is the developer’s responsibility to ensure that any data derived from JWT claims and placed into headers is properly encoded or restricted.
Real-world attack patterns mirror this: an attacker crafts a JWT with newline characters in string claims, submits it to an endpoint that reflects claims in headers, and observes whether the server splits the response. Tools that scan for CRLF injection will flag the presence of unsanitized header outputs. Because JWTs are often used for authorization, a successful injection can escalate impact by smuggling cookies or headers that bypass intended controls.
middleBrick scans for CRLF injection by testing header and cookie reflection points, including values derived from JWT claims, and flags locations where unsanitized input reaches HTTP messages. Findings include the exact vector, severity, and remediation guidance mapped to OWASP API Top 10 and related standards.
Jwt Tokens-Specific Remediation in Actix — concrete code fixes
Remediation focuses on strict validation and safe handling of any data derived from JWT tokens before it is used in HTTP headers, Set-Cookie, or redirect locations. Never directly interpolate claims into headers; instead, enforce allowlists, encode newlines, and avoid reflecting untrusted data.
Example of vulnerable code that reflects a JWT claim into a header:
use actix_web::{web, HttpResponse, Responder};
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
async fn vulnerable(
token: web::Query,
) -> impl Responder {
// Assume TokenClaim has a field `username`
let username = &token.username;
// UNSAFE: directly using user input in a header
HttpResponse::Ok()
.insert_header(("X-Username", username.as_str()))
.finish()
}
This is unsafe because username may contain CRLF sequences. An attacker can craft a token with username: foo\r\nSet-Cookie: auth=evil and split the response.
Safe remediation — validate and encode the claim:
use actix_web::{web, HttpResponse, Responder};
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
fn is_safe_header_value(value: &str) -> bool {
// Reject CR and LF characters
!value.contains('\r') && !value.contains('\n')
}
async fn safe(
token: web::Query,
) -> impl Responder {
let username = &token.username;
if !is_safe_header_value(username) {
return HttpResponse::BadRequest().body("Invalid username");
}
HttpResponse::Ok()
.insert_header(("X-Username", username.as_str()))
.finish()
}
When setting cookies from JWT claims, avoid constructing Set-Cookie manually; prefer Actix’s Cookie builder which handles encoding. Example of safe cookie handling:
use actix_web::{web, HttpResponse, cookie::Cookie, Responder};
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
async fn set_cookie_safe(
token: web::Query,
) -> impl Responder {
let username = &token.username;
if !is_safe_header_value(username) {
return HttpResponse::BadRequest().body("Invalid username");
}
let cookie = Cookie::build(("username", username.to_string()))
.http_only(true)
.secure(true)
.finish();
HttpResponse::Ok()
.cookie(cookie)
.finish()
}
For redirects, validate and sanitize the target URL and do not embed raw JWT claims in the Location header. Use a whitelist of allowed redirect paths or a mapping instead of direct reflection:
use actix_web::{web, HttpResponse, Responder};
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
async fn redirect_safe(
token: web::Query,
) -> impl Responder {
let target = match token.redirect.as_str() {
"dashboard" => "/dashboard",
"profile" => "/profile",
_ => "/",
};
HttpResponse::Found()
.insert_header(("Location", target))
.finish()
}
Additionally, enforce strong validation on JWTs using a reputable library (e.g., jsonwebtoken) and verify issuer, audience, and expiration. Do not trust claims for security decisions without verification, and avoid placing raw JWT payload data into headers where it can be smuggled.
middleBrick’s CLI can be used to verify remediation: middlebrick scan <url>. The GitHub Action can enforce a minimum score before deployment, and the MCP Server allows scanning from IDEs to catch header injection issues early.