Header Injection in Actix with Basic Auth
Header Injection in Actix with Basic Auth — how this specific combination creates or exposes the vulnerability
Header Injection occurs when user-controlled data is placed directly into HTTP headers without validation or sanitization. In Actix web applications that use HTTP Basic Authentication, this risk arises when values from request inputs—such as query parameters, headers, or body fields—are used to construct or influence response headers while handling authentication. For example, if an endpoint reads a username or a token from a request header and then passes it to a function that sets a custom response header, an attacker may inject newline characters (CRLF, i.e., \r\n) to split the header and inject additional headers like Location, Set-Cookie, or X-Content-Type-Options.
When Basic Auth is involved, the Authorization header is typically parsed to extract credentials. If the application uses these credentials to influence other headers (for instance, logging or adding custom metadata), and does not enforce strict validation, newline characters in the username or password fields can lead to header manipulation. Although Basic Auth credentials are base64-encoded in the header value, decoding and using them in downstream logic without sanitization can introduce injection points. Attackers may exploit this to perform HTTP response splitting, which can lead to cache poisoning, cross-site scripting in certain contexts, or bypassing intended routing logic.
Consider an Actix service that decodes the Basic Auth header and uses the username in a custom response header for debugging. A request like Authorization: Basic dGVzdA== (decodes to test) might be reflected in a header such as X-User: test. If an attacker sends a username containing \r\nX-Admin: true, the resulting headers can split and inject new headers, potentially altering the response handling in the client or intermediary proxies. The vulnerability is not in Basic Auth itself but in how the extracted data is handled when constructing headers. Proper input validation, avoiding direct reflection of user-derived values into headers, and using framework-provided mechanisms for header management are essential to mitigate this risk in Actix-based services.
Basic Auth-Specific Remediation in Actix — concrete code fixes
To prevent Header Injection in Actix when using Basic Auth, ensure that any user-derived data—especially from credentials or headers—is not directly reflected into response headers. Always validate and sanitize inputs, and use Actix's built-in abstractions for managing authentication and headers rather than manually constructing header values.
Below are concrete remediation examples with valid Actix code snippets.
1. Avoid reflecting Basic Auth credentials in headers
Do not extract the username or password from the Authorization header and set it directly in a response header. Instead, if you need to associate request context, use request extensions or local data that are not exposed to the client.
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use actix_web::http::header::HeaderValue;
use actix_web::dev::ServiceRequest;
use std::str::from_utf8;
// Unsafe example to avoid: reflecting decoded username into a header
async fn unsafe_endpoint(req: ServiceRequest) -> impl Responder {
if let Some(auth_header) = req.headers().get("Authorization") {
if let Ok(auth_str) = auth_header.to_str() {
if auth_str.starts_with("Basic ") {
let encoded = &auth_str[6..];
if let Ok(decoded) = base64::decode(encoded) {
if let Ok(credentials) = from_utf8(&decoded) {
let parts: Vec<&str>> = credentials.split(':').collect();
if parts.len() == 2 {
let username = parts[0];
// Vulnerable: directly using user input in a header
return HttpResponse::Ok()
.insert_header(("X-User", username))
.body("Processed");
}
}
}
}
}
}
HttpResponse::Unauthorized().body("Missing or invalid auth")
}
// Safe alternative: do not reflect user data in headers
async fn safe_endpoint(req: ServiceRequest) -> impl Responder {
// Perform authentication but do not expose credentials in headers
if let Some(auth_header) = req.headers().get("Authorization") {
if let Ok(auth_str) = auth_header.to_str() {
if auth_str.starts_with("Basic ") {
let encoded = &auth_str[6..];
if base64::decode(encoded).is_ok() {
// Authentication succeeded, proceed without reflecting user data
return HttpResponse::Ok().body("Authenticated and safe");
}
}
}
}
HttpResponse::Unauthorized().body("Missing or invalid auth")
}
2. Validate and sanitize any custom headers derived from request data
If your application sets custom headers based on request parameters or decoded credentials, enforce strict allowlists and reject inputs containing CRLF sequences.
fn is_valid_header_value(value: &str) -> bool {
// Reject newline characters to prevent header injection
!value.contains('\r') && !value.contains('\n')
}
async fn endpoint_with_custom_header(req: ServiceRequest) -> impl Responder {
let param = req.query_string(); // Example: extract a safe parameter
// Assume we derive a header value from a query parameter 'tag'
if let Some(tag) = param.split('&').find_map(|p| p.strip_prefix("tag=")) {
if is_valid_header_value(tag) {
return HttpResponse::Ok()
.insert_header(("X-Custom-Tag", tag))
.body("OK");
}
}
HttpResponse::BadRequest().body("Invalid parameter")
}
3. Use middleware for centralized header validation
Implement a lightweight middleware that checks outgoing headers for injected content. This ensures consistent protection across all endpoints using Basic Auth.
use actix_web::{dev::ServiceResponse, Error};
use actix_web::body::BoxBody;
async fn validate_outgoing_headers(res: ServiceResponse) -> Result, Error> {
let headers = res.headers();
for (name, value) in headers.iter() {
if let Ok(val_str) = value.to_str() {
if val_str.contains('\r') || val_str.contains('\n') {
// Log and reject the response to prevent injection
return Err(actix_web::error::ErrorBadRequest("Invalid header value"));
}
}
}
Ok(res)
}
By following these practices—avoiding reflection of credentials, validating inputs rigorously, and centralizing header checks—you can effectively mitigate Header Injection risks in Actix applications using Basic Auth.