HIGH cross site request forgeryactixhmac signatures

Cross Site Request Forgery in Actix with Hmac Signatures

Cross Site Request Forgery in Actix with Hmac Signatures — how this specific combination creates or exposes the vulnerability

Cross Site Request Forgery (CSRF) in Actix applications that rely on Hmac Signatures can still occur when the signature is computed over a subset of the request that an attacker can predict or influence. Hmac Signatures typically protect integrity by signing a canonical representation of the request (e.g., selected headers, method, path, and body). If the server validates the signature but does not also enforce a same-origin or anti-CSRF binding tied to the user’s session, an attacker can trick a victim’s browser into issuing a request with a valid signature but malicious parameters.

For example, consider an Actix endpoint that transfers funds using a POST with a JSON body and an Hmac-SHA256 signature in a custom header. If the signature covers only the JSON body and the server trusts the Referer or Origin headers to determine intent, an attacker can host a malicious page that submits a forged POST with a valid body (e.g., {"to":"attacker","amount":1000}) and a correct Hmac computed with the compromised key. Because Actix middleware may validate the signature without also checking a per-request nonce or a SameSite cookie binding, the request appears legitimate.

In an API context, CSRF-like behavior can also arise when an Hmac-signed request is replayed from a different origin without additional anti-replay controls. If the signature lacks a timestamp or nonce and the endpoint performs state-changing actions, an attacker can reuse a captured request. The risk is higher when the signing key is embedded in client-side code or when the signature is computed over mutable headers that an attacker can partially control. The OpenAPI/Swagger analysis provided by middleBrick can surface missing binding between the signature scheme and anti-CSRF requirements, highlighting endpoints where authentication is present but effective CSRF protection is not enforced.

Another subtle exposure occurs when CORS is misconfigured alongside Hmac Signatures. If an endpoint accepts cross-origin requests and relies solely on Hmac for integrity without requiring credentials mode omission or explicit origin checks, an attacker can leverage a compromised browser context to make signed requests on behalf of the user. middleBrick’s checks for Authentication and BOLA/IDOR often reveal inconsistencies where signatures are validated without correlating the request source to an authenticated user’s session context, increasing the likelihood of unauthorized operations.

Hmac Signatures-Specific Remediation in Actix — concrete code fixes

To mitigate CSRF and replay risks when using Hmac Signatures in Actix, bind the signature to a per-request nonce or timestamp and enforce SameSite cookie attributes or anti-CSRF tokens for state-changing operations. Include a nonce or timestamp in the signed payload and verify it on the server before processing any side effects. Ensure the signature covers the HTTP method, path, selected headers, and the nonce/timestamp, preventing attackers from reusing or injecting valid signed requests.

Example: Hmac-Signed POST with nonce in Actix (Rust)

use actix_web::{web, HttpRequest, HttpResponse, Result};
use hmac::{Hmac, Mac};
use sha2::Sha256;
use std::collections::HashSet;

// Compute Hmac-SHA256 over method, path, nonce, and body
fn compute_signature(
    key: &[u8],
    method: &str,
    path: &str,
    nonce: &str,
    body: &str,
) -> String {
    let mut mac = Hmac::::new_from_slice(key).expect("valid key");
    mac.update(method.as_bytes());
    mac.update(b":");
    mac.update(path.as_bytes());
    mac.update(b":");
    mac.update(nonce.as_bytes());
    mac.update(b":");
    mac.update(body.as_bytes());
    let result = mac.finalize();
    hex::encode(result.into_bytes())
}

// Validate request: check signature and nonce freshness
async fn validate_request(
    req: &HttpRequest,
    body: &str,
    nonce_seen: &mut HashSet,
) -> bool {
    let method = req.method().as_str();
    let path = req.path();
    let headers = req.headers();
    let received_nonce = match headers.get("X-Request-Nonce") {
        Some(v) => match v.to_str() {
            Ok(s) => s,
            Err(_) => return false,
        },
        None => return false,
    };
    let received_sig = match headers.get("X-Hmac-Signature") {
        Some(v) => match v.to_str() {
            Ok(s) => s,
            Err(_) => return false,
        },
        None => return false,
    };
    // Prevent replay: ensure nonce has not been used
    if !nonce_seen.insert(received_nonce.to_string()) {
        return false;
    }
    let key = b"super-secret-key-32bytes-long-1234567890ab";
    let expected_sig = compute_signature(key, method, path, received_nonce, body);
    // Constant-time comparison recommended in production
    received_sig == expected_sig
}

// Actix handler demonstrating usage
async fn transfer_funds(
    req: HttpRequest,
    body: String,
    nonce_seen: web::Data>>,
) -> Result {
    let mut nonces = nonce_seen.lock().unwrap();
    if !validate_request(&req, &body, &mut nonces) {
        return Ok(HttpResponse::Forbidden().body("Invalid signature or replay detected"));
    }
    // Parse and process transfer safely
    let payload: serde_json::Value = serde_json::from_str(&body).map_err(|_| HttpResponse::BadRequest())?;
    // ... business logic
    Ok(HttpResponse::Ok().body("Transfer accepted"))
}

In this example, the nonce (X-Request-Nonce) is included in the Hmac computation and checked for uniqueness to prevent replay. The signature covers method, path, nonce, and body, ensuring that a forged request with modified parameters will not produce a valid signature unless the attacker knows the key. For browser-initiated requests, pair this with SameSite=Strict or Lax cookies and an anti-CSRF token in a header to block cross-origin submissions even if the signature scheme is bypassed.

Example: Middleware verification in Actix-web (Rust)

use actix_web::{dev::ServiceRequest, Error};
use actix_web_httpauth::extractors::bearer::BearerAuth;

// Custom signature validation guard
pub async fn verify_hmac(req: ServiceRequest) -> Result {
    let (req, pl) = req.into_parts();
    // Extract headers and body as in validate_request above
    // If validation fails, return 403
    // Otherwise reconstruct ServiceRequest and proceed
    Ok(actix_web::dev::ServiceRequest::from_parts(req, pl))
}

Additionally, rotate signing keys periodically and store them securely. Use environment variables or a secrets manager rather than embedding keys in client-side code. If you use middleBrick’s CLI (middlebrick scan ) or GitHub Action to integrate API security checks into CI/CD, you can automatically detect endpoints that accept Hmac-signed requests without nonce or SameSite protections, helping you enforce consistent CSRF defenses across your API surface.

Frequently Asked Questions

Can an Hmac Signature alone prevent CSRF in Actix APIs?
No. Hmac Signatures ensure integrity and authenticity of the request content, but they do not bind the request to a user’s browser context. Without additional protections such as SameSite cookies, anti-CSRF tokens, or per-request nonces, a malicious site can still induce a victim’s browser to make a valid signed request.
What should be included in the signed payload to reduce CSRF risk?
Include the HTTP method, request path, a unique nonce or timestamp, and the body. This prevents attackers from replaying or modifying critical parameters while preserving the signature’s integrity. Ensure the nonce is checked server-side to prevent reuse.