Header Injection in Actix with Bearer Tokens
Header Injection in Actix with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Header Injection in Actix applications using Bearer Tokens occurs when user-controlled input is reflected into HTTP headers, particularly the Authorization header or custom headers that downstream services use for token validation. Because Actix is a Rust web framework that relies on strongly typed extractors, developers may assume that header parsing is safe, but if header values are constructed by concatenating or interpolating user input, an attacker can inject additional header lines or manipulate the Authorization scheme.
Bearer Token vulnerabilities in this context typically involve an API endpoint that accepts a token via a query parameter or a custom header and then forwards it to an internal service without proper validation. For example, an endpoint like /proxy might read a token from X-API-Key and use it to call another service, but if the value is not strictly validated, an attacker can inject a newline character (%0A or %0D) to append a second Authorization header. This can lead to privilege escalation if the injected token is accepted by the backend, or it can bypass intended routing logic.
In the context of OAuth 2.0 and Bearer Tokens, the Authorization header follows the format Authorization: Bearer <token>. If an Actix handler builds this header dynamically using string formatting and includes untrusted data, an attacker might supply a token like abc%0AAuthorization:%20Bearer%20evil. When the request is forwarded, the injected line creates a second Authorization header, potentially allowing the attacker to present a valid-looking token that the backend mistakenly trusts. This is a form of HTTP request smuggling or header splitting, depending on how the downstream service parses the message.
Real-world impact includes unauthorized access to protected resources, token theft via reflected outputs, and bypassing rate limiting or authentication middleware. Because middleBrick tests for Input Validation and Authentication in parallel, it can detect anomalies where header values contain unexpected line breaks or where the Authorization header appears more than once in a parsed request. These findings are mapped to the OWASP API Top 10 and relevant compliance frameworks, highlighting the need for strict header validation.
An example of a vulnerable Actix handler that concatenates user input into a header might look like this, though note that the issue is not with Actix itself but with how developers use it:
// Vulnerable pattern: do not construct headers by concatenating user input
async fn proxy_handler(token: web::Query<TokenInput>) -> impl Responder {
let custom_header = format!("Bearer {}", token.value);
// Forwarding logic that uses custom_header unsafely
HttpResponse::Ok().body("proxied")
}
In this snippet, token.value comes directly from user-controlled query parameters. If not sanitized, it can contain newline characters that alter the header structure. middleBrick’s active probing includes sequences that attempt such injections and checks for reflected anomalies, including duplicate headers or malformed routing decisions.
Bearer Tokens-Specific Remediation in Actix — concrete code fixes
Remediation focuses on preventing user input from ever reaching the header construction logic. In Actix, use strongly typed extractors and avoid string interpolation for Authorization values. Instead of building headers manually, validate tokens against a known set of patterns or use middleware to enforce structure.
First, define a strict validation function that rejects any input containing control characters or newline sequences. Then, use the validated token to set headers via Actix's HeaderMap, which ensures proper encoding and prevents line splitting.
// Secure pattern: validate and use tokens without concatenation
use actix_web::{web, HttpRequest, HttpResponse, Responder};
use actix_web::http::header::{HeaderValue, AUTHORIZATION};
use regex::Regex;
fn is_valid_bearer_token(token: &str) -> bool {
// Allow only alphanumeric, hyphen, underscore, period, and base64url chars
let re = Regex::new(r"^[A-Za-z0-9\-._~+/=]+$").unwrap();
re.is_match(token) && !token.contains('\n') && !token.contains('\r')
}
async fn secure_handler(req: HttpRequest, token: web::Query<TokenInput>) -> impl Responder {
let token_str = token.value.trim();
if !is_valid_bearer_token(token_str) {
return HttpResponse::BadRequest().body("invalid token");
}
// Properly set Authorization header using HeaderMap
let mut headers = req.headers().clone();
headers.insert(
AUTHORIZATION,
HeaderValue::from_str(&format!("Bearer {}", token_str)).unwrap(),
);
// Continue with safe request handling
HttpResponse::Ok().body("secure proxied request")
}
Second, leverage Actix middleware to sanitize incoming headers globally. This ensures that even if multiple handlers are used, the Authorization header is normalized before reaching business logic.
// Middleware example to strip dangerous characters from headers
use actix_web::dev::{ServiceRequest, ServiceResponse, Transform};
use actix_web::Error;
use futures::future::{ok, Ready};
pub struct HeaderSanitizer;
impl Transform<S, ServiceRequest> for HeaderSanitizer
where
S: actix_web::dev::Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<B>;
type Error = Error;
type InitError = ();
type Transform = HeaderSanitizerMiddleware<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ok(HeaderSanitizerMiddleware { service })
}
}
pub struct HeaderSanitizerMiddleware<S> {
service: S,
}
impl<S, B> actix_web::dev::Service<ServiceRequest> for HeaderSanitizerMiddleware<S>
where
S: actix_web::dev::Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<B>;
type Error = Error;
type Future = S::Future;
fn poll_ready(&self, cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<Self::Ready, Self::Error>> {
self.service.poll_ready(cx)
}
fn call(&self, mut req: ServiceRequest) -> Self::Future {
if let Some(hdr) = req.headers_mut().get_mut("authorization") {
if let Ok(val) = hdr.to_str() {
let sanitized = val.replace(['\n', '\r'], "");
*hdr = HeaderValue::from_str(&sanitized).unwrap_or_else(|_| HeaderValue::from_static(""));
}
}
self.service.call(req)
}
}
Finally, prefer using environment variables or secure configuration for static tokens, and avoid reflecting any user-supplied value into the Authorization header. middleBrick’s LLM/AI Security checks can identify endpoints that expose token handling logic through prompt injection tests, reinforcing the need for strict input validation.