Missing Tls in Actix with Hmac Signatures
Missing Tls in Actix with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Using HMAC signatures for request authentication in Actix without enforcing TLS exposes the signature and secret material to network-level interception. HMAC relies on a shared secret and a deterministic algorithm (e.g., HMAC-SHA256) to sign requests; if the transport is unencrypted, an on-path attacker can observe the signature, the payload, and potentially recover or brute-force the secret depending on implementation details.
In an Actix-based service, if routes that verify HMAC are served over plain HTTP, a missing TLS configuration means no integrity protection exists between client and server. Attackers can capture requests in transit, replay them, or tamper with the message body while keeping the signature valid if the server does not also validate additional context (e.g., timestamps or nonces). This violates authentication and integrity guarantees that HMAC is intended to provide.
The risk is especially critical when signatures are used to authorize privileged operations or to bind requests to a particular identity. Without TLS, intercepted signed requests can be used in replay attacks or used to escalate privileges if the server logic does not enforce strict transport security. middleBrick scans for this issue under the Encryption and Authentication checks, noting that unencrypted endpoints with signature validation create a false sense of security.
Because middleBrick tests the unauthenticated attack surface, it can detect missing TLS on endpoints that use HMAC-based schemes and surface this as a high-severity finding. The tool does not attempt to fix the deployment, but it highlights the need to terminate TLS before requests reach your Actix application and to ensure strict transport policies are enforced.
Hmac Signatures-Specific Remediation in Actix — concrete code fixes
To remediate missing TLS for HMAC-signed requests in Actix, enforce HTTPS at the edge and implement robust signature verification within your handlers. Below are concrete examples that show how to configure TLS and validate HMAC-SHA256 signatures in Actix.
Enforce HTTPS in Actix
Use Rust native TLS support via actix-web::web::ServiceConfig and an HTTPS connector. A minimal secure server setup:
use actix_web::{web, App, HttpServer, Responder};
use actix_web::http::header;
use actix_web::middleware::Logger;
use rustls::ServerConfig;
use std::sync::Arc;
use tokio_rustls::TlsAcceptor;
async fn index() -> impl Responder {
"OK"
}
#[actix_web::main]
async fn main() -> std::io::Result<()>) {
// Configure TLS using rustls (load cert/key via files or other secure stores)
let mut config = ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(
vec![std::fs::read("cert.pem").expect("invalid cert")],
rustls::PrivateKey(std::fs::read("key.pem").expect("invalid key")),
)
.expect("bad certificate/key");
config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
let tls_acceptor = TlsAcceptor::from(Arc::new(config));
HttpServer::new(|| {
App::new()
.wrap(Logger::default())
.service(web::resource("/api/endpoint").to(index))
})
.bind_rustls("0.0.0.0:8443", tls_acceptor)?
.run()
.await
}
HMAC Signature Verification in Actix Handler
Validate the signature on each request using a constant-time comparison and include nonce/timestamp checks to prevent replay.
use actix_web::{web, HttpRequest, HttpResponse, Result};
use hmac::{Hmac, Mac};
use sha2::Sha256;
use std::time::{SystemTime, UNIX_EPOCH};
type HmacSha256 = Hmac;
async fn verify_hmac(req: HttpRequest, body: String) -> Result {
// Retrieve headers
let signature_header = match req.headers().get("X-API-Signature") {
Some(h) => h.to_str().map_err(|_| HttpResponse::BadRequest().finish())?,
None => return Ok(HttpResponse::BadRequest().body("Missing signature")),
};
let timestamp = match req.headers().get("X-Request-Timestamp") {
Some(h) => h.to_str().map_err(|_| HttpResponse::BadRequest().finish())?,
None => return Ok(HttpResponse::BadRequest().body("Missing timestamp")),
};
// Basic replay protection: reject if timestamp too old (e.g., 5 minutes)
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
let req_time: u64 = timestamp.parse().map_err(|_| HttpResponse::BadRequest().finish())?;
if now.saturating_sub(req_time) > 300 {
return Ok(HttpResponse::BadRequest().body("Request expired"));
}
// Reconstruct the signing string as the client did (e.g., method + path + body + timestamp)
let signing_string = format!("{}:{}:{}", req.method(), req.path(), body);
let secret = std::env::var("HMAC_SECRET").expect("HMAC_SECRET must be set");
let mut mac = HmacSha256::new_from_slice(secret.as_bytes())
.expect("HMAC can take key of any size");
mac.update(signing_string.as_bytes());
let computed = mac.finalize().into_bytes();
// Decode client signature (hex or base64 as agreed)
let client_sig = hex::decode(signature_header)
.map_err(|_| HttpResponse::BadRequest().body("Invalid signature encoding"))?;
// Constant-time comparison
if computed.as_slice() == client_sig.as_slice() {
Ok(HttpResponse::Ok().body("Authorized"))
} else {
Ok(HttpResponse::Forbidden().body("Invalid signature"))
}
}
Ensure TLS is enforced at the load balancer or reverse proxy as well, and rotate HMAC secrets periodically. middleBrick’s Pro plan can add continuous monitoring so that future scans flag missing TLS on authenticated endpoints automatically.
Related CWEs: encryption
| CWE ID | Name | Severity |
|---|---|---|
| CWE-319 | Cleartext Transmission of Sensitive Information | HIGH |
| CWE-295 | Improper Certificate Validation | HIGH |
| CWE-326 | Inadequate Encryption Strength | HIGH |
| CWE-327 | Use of a Broken or Risky Cryptographic Algorithm | HIGH |
| CWE-328 | Use of Weak Hash | HIGH |
| CWE-330 | Use of Insufficiently Random Values | HIGH |
| CWE-338 | Use of Cryptographically Weak PRNG | MEDIUM |
| CWE-693 | Protection Mechanism Failure | MEDIUM |
| CWE-757 | Selection of Less-Secure Algorithm During Negotiation | HIGH |
| CWE-261 | Weak Encoding for Password | HIGH |