HIGH header injectionactixhmac signatures

Header Injection in Actix with Hmac Signatures

Header Injection in Actix with Hmac Signatures — how this specific combination creates or exposes the vulnerability

Header Injection in Actix applications that use Hmac Signatures occurs when untrusted input from HTTP headers is incorporated into the data that is signed or verified without proper validation and canonicalization. Actix web is a Rust framework where routes and middleware handle headers explicitly; if developer code reads headers such as x-forwarded-user or x-request-id and includes those values in the payload that is Hmac-verified, an attacker can inject additional header lines or new fields that alter the signed context.

Hmac Signatures rely on a deterministic byte sequence: the exact concatenation of selected headers, a payload, and a secret. If the server builds the signing string by naively concatenating header values as received, an attacker who can control a header can introduce line breaks or additional key-value pairs (e.g., injecting X-User: attacker or breaking the header block with \n). Because Actix middleware can expose headers early and pass them to business logic, an attacker can manipulate the signed material without knowing the secret, leading to authentication bypass or privilege escalation.

This combination is risky because Hmac protects integrity, not authenticity of the header source. The framework does not inherently prevent header values from containing newline characters; if the signing routine does not enforce a strict header canonicalization policy (e.g., reject multi-line headers, normalize whitespace, and enforce a predefined ordering), an attacker can forge a valid Hmac by injecting crafted headers. Real-world impact includes bypassing route-level authentication that depends on Hmac-signed tokens passed via headers, and escalating privileges when injected headers are trusted for authorization decisions.

For example, an Actix service might compute Hmac over username:timestamp:nonce taken from headers. If the username header is user-controlled and the service does not validate that it contains no line breaks, an attacker can inject a second username line, causing the server to verify a different identity while producing a valid Hmac. This mirrors classic HTTP request smuggling and header contamination patterns, and it is detectable by middleBrick’s Authentication and BOLA/IDOR checks when scanning the endpoint.

middleBrick scans such endpoints in black-box mode, testing unauthenticated attack surfaces and flagging inconsistencies between spec-defined header usage and runtime behavior. If your API exposes Hmac-signed routes, the scan can surface findings related to Input Validation and Authentication, with remediation guidance focused on canonicalization and strict header parsing.

Hmac Signatures-Specific Remediation in Actix — concrete code fixes

Remediation centers on strict header validation, canonicalization, and avoiding the inclusion of untrusted header values in the Hmac-signed string. In Actix, implement a middleware or extractor that normalizes and validates headers before they reach the signing or verification logic. The following examples show a safe approach using the actix-web framework with hmac and sha2 crates.

1. Define a canonical header extraction function

Reject headers with unexpected line breaks and enforce lowercase keys for a deterministic order. For a username provided via a custom header, validate it strictly:

use actix_web::{dev::Payload, Error, HttpRequest};
use actix_web::http::header::HeaderValue;
use std::str;

fn canonical_username(req: &HttpRequest) -> Result<String, Error> {
    // Reject if header is missing
    let value = req.headers().get("x-username")
        .ok_or_else(|| actix_web::error::ErrorBadRequest("missing x-username"))?;
    let raw = value.to_str().map_err(|_| actix_web::error::ErrorBadRequest("invalid encoding"))?;
    // Reject newline or carriage return to prevent header injection
    if raw.contains('\n') || raw.contains('\r') {
        return Err(actix_web::error::ErrorBadRequest("invalid username header"));
    }
    // Optionally enforce a safe pattern (alphanumeric + limited symbols)
    let username = raw.trim();
    if username.is_empty() {
        return Err(actix_web::error::ErrorBadRequest("empty username"));
    }
    Ok(username.to_string())
}

2. Build the Hmac-signed payload from canonicalized values

Use a deterministic format and a fixed ordering. Include a nonce and timestamp to prevent replay, and ensure no extra whitespace or delimiters can be injected:

use hmac::{Hmac, Mac};
use sha2::Sha256;
use std::time::{SystemTime, UNIX_EPOCH};

type HmacSha256 = Hmac<Sha256>;

fn build_payload(username: &str, secret: &[u8]) -> (Vec<u8>, String) {
    let timestamp = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .expect("time")
        .as_secs()
        .to_string();
    let nonce = uuid::Uuid::new_v4().to_string();
    // Canonical format: username:timestamp:nonce
    let message = format!("{}:{}:{}", username, timestamp, nonce);
    let mut mac = HmacSha256::new_from_slice(secret).expect("valid key");
    mac.update(message.as_bytes());
    let result = mac.finalize();
    let signature = hex::encode(result.into_bytes());
    (message.into_bytes(), signature)
}

3. Verify with the same canonicalization and reject unexpected headers

During verification, re-derive the payload from the canonicalized header values and compare the Hmac in constant time. Do not concatenate raw header strings as received:

fn verify_request(
    req: &HttpRequest,
    expected_username: &str,
    signature: &str,
    secret: &[u8],
) -> bool {
    let username = match canonical_username(req) {
        Ok(u) => u,
        _ => return false,
    };
    if username != expected_username {
        return false;
    }
    let timestamp = match req.headers().get("x-timestamp") {
        Some(v) => v.to_str().unwrap_or(""),
        None => return false,
    };
    let nonce = match req.headers().get("x-nonce") {
        Some(v) => v.to_str().unwrap_or(""),
        None => return false,
    };
    let message = format!("{}:{}:{}", username, timestamp, nonce);
    let mut mac = HmacSha256::new_from_slice(secret).expect("valid key");
    mac.update(message.as_bytes());
    match mac.verify_slice(hex::decode(signature).ok()?.as_ref()) {
        Ok(_) => true,
        Err(_) => false,
    }
}

4. Middleware to enforce header policies

Add an Actix middleware layer that rejects requests containing newline characters in selected headers and normalizes case. This ensures that downstream handlers never see unsafe values that could affect Hmac computation:

use actix_web::{Error, dev::{ServiceRequest, ServiceResponse, Transform}, ErrorHeader};
use futures::future::{ok, Ready};

pub struct HeaderSanitization;

impl Transform for HeaderSanitization
where
    S: actix_service::Service, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse;
    type Error = Error;
    type Transform = HeaderSanitizer;
    type InitError = ();
    type Future = Ready>;

    fn new_transform(&self, service: S) -> Self::Future {
        ok(HeaderSanitizer { service })
    }
}

pub struct HeaderSanitizer {
    service: S,
}

impl actix_service::Service for HeaderSanitizer
where
    S: actix_service::Service, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse;
    type Error = Error;
    type Future = futures::future::LocalBoxFuture<'static, Result>;

    actix_service::forward_ready!(service);

    fn call(&self, req: ServiceRequest) -> Self::Future {
        // Reject headers with newlines to prevent injection
        for (key, value) in req.headers() {
            if let Ok(v) = value.to_str() {
                if v.contains('\n') || v.contains('\r') {
                    let res = actix_web::error::ErrorBadRequest("newline in header").into();
                    return Box::pin(async { Err(res) });
                }
            }
        }
        let fut = self.service.call(req);
        Box::pin(async move {
            let res = fut.await?;
            Ok(res)
        })
    }
}

By canonicalizing usernames, timestamps, and nonces, and by rejecting headers containing control characters, the Hmac signature remains tied to server-controlled canonical values. This prevents attackers from exploiting header injection to corrupt the signed payload.

middleBrick can validate these defenses by running its scans and checking that inputs like newline-injected usernames are rejected and that Hmac verification fails safely. Use the CLI (middlebrick scan <url>) or the GitHub Action to enforce these checks in CI/CD, and review remediation guidance in the dashboard.

Frequently Asked Questions

Can header injection bypass Hmac-signed routes even if the secret is unknown?
Yes, if the server includes attacker-controlled header values directly in the Hmac-signed string without canonicalization or rejection of newlines, an attacker can extend the signed payload and forge a valid Hmac.
Does middleBrick test for header injection in Hmac-signed APIs?
Yes, middleBrick’s Input Validation and Authentication checks include tests that probe for header injection and signature misuse; findings appear in the dashboard with remediation guidance.