Heap Overflow in Actix with Hmac Signatures
Heap Overflow in Actix with Hmac Signatures — how this specific combination creates or exposes the vulnerability
A heap overflow in an Actix web service that uses HMAC signatures can occur when unchecked or oversized data feeds into buffers or heap‑allocated structures during signature verification. In Actix, HTTP payloads and headers are parsed into Rust data structures; if the input that eventually participates in HMAC verification is derived from untrusted sources without proper length or type checks, the runtime representation may allocate or index beyond safe bounds.
Consider a scenario where a JSON body contains a field used both for business logic and for an HMAC signature computed over selected fields. If an attacker sends an extremely large string in that field, Actix’s deserialization layer may allocate a large buffer on the heap. Even if later checks reject oversized content, the allocation itself can stress the heap or expose patterns that facilitate further exploitation. Moreover, if the HMAC verification code processes attacker-controlled byte slices without strict length validation before using them in index arithmetic or copying into fixed‑size arrays, the heap layout can be corrupted or out‑of‑bounds accesses may occur.
Another angle is the use of variable‑length inputs in signature computation. For example, concatenating untrusted user input into the data that is signed can increase the size of the signed payload unpredictably. If the code that builds the message for HMAC uses unbounded collections (e.g., growing a Vec from untrusted parts) and then computes the signature over the resulting bytes, an oversized message can lead to heap exhaustion or memory corruption. While Rust’s safety guarantees prevent some classes of overflows, incorrect use of indexing, raw pointers, or unsafe blocks in verification logic can reintroduce heap‑overflow risks.
In the context of middleBrick’s 12 security checks, unbounded input validation and unsafe consumption are flagged as relevant controls. The scanner tests whether the API parses and validates message size and structure before performing cryptographic operations like HMAC verification. Findings in these areas highlight where untrusted data reaches the code path that handles signatures, emphasizing the need to bound and sanitize inputs before they influence heap allocations or indexing.
Hmac Signatures-Specific Remediation in Actix — concrete code fixes
Remediation centers on strict input validation, bounded buffers, and safe APIs when constructing and verifying HMAC signatures in Actix. Always validate the length and format of any data that participates in signature computation before using it. Prefer fixed‑size buffers for signatures and avoid concatenating unbounded user input into the signed payload.
Example of a safe HMAC‑SHA256 verification flow in Actix using constant‑time comparison to avoid timing leaks and strict length checks on inputs:
use actix_web::{post, web, HttpResponse, Result};
use hmac::{Hmac, Mac};
use sha2::Sha256;
use hex;
type HmacSha256 = Hmac<Sha256>;
/// Verify a request where the client sends JSON with a "data" field and an "hmac" field.
#[post("/verify")]
async fn verify(
payload: web::Json<serde_json::Value>,
) -> Result<HttpResponse> {
// 1) Validate expected structure and size limits before processing.
let data = payload.get("data")
.and_then(|v| v.as_str())
.ok_or_else(|| HttpResponse::BadRequest().body("missing or invalid data"))?;
let received_mac = payload.get("hmac")
.and_then(|v| v.as_str())
.ok_or_else(|| HttpResponse::BadRequest().body("missing or invalid hmac"))?;
// 2) Enforce a reasonable upper bound on data length to prevent heap abuse.
const MAX_DATA_LEN: usize = 4096;
if data.len() > MAX_DATA_LEN {
return Ok(HttpResponse::PayloadTooLarge().body("data too large"));
}
// 3) Compute HMAC using a fixed key (in practice, load securely, e.g., from env).
let key = b"my-secret-key-32-bytes-long-for-demo";
let mut mac = HmacSha256::new_from_slice(key)
.map_err(|_| HttpResponse::InternalServerError().body("hmac init error"))?;
mac.update(data.as_bytes());
// 4) Decode the received hex MAC safely and compare in constant time.
let computed_mac = hex::decode(received_mac)
.map_err(|_| HttpResponse::BadRequest().body("invalid hmac format"))?;
// Use verify_slice which performs constant‑time comparison.
match mac.verify_slice(&computed_mac) {
Ok(_) => Ok(HttpResponse::Ok().body("verified")),
Err(_) => Ok(HttpResponse::Unauthorized().body("invalid signature")),
}
}
If you accept structured input for signing, ensure that serialization does not introduce unbounded growth. For example, when signing selected fields, explicitly construct a canonical representation rather than signing the entire mutable JSON object:
use actix_web::{post, web, HttpResponse, Result};
use hmac::{Hmac, Mac};
use sha2::Sha256;
use serde::{Deserialize, Serialize};
type HmacSha256 = Hmac<Sha256>;
#[derive(Deserialize, Serialize)]
struct SignedPayload {
user_id: u64,
action: String,
#[serde(skip_serializing)] // not included in signature
nonce: String,
}
#[post("/signed")]
async fn handle_signed(
payload: web::Json<SignedPayload>,
) -> Result<HttpResponse> {
const MAX_USER_ID: u64 = 1_000_000;
if payload.user_id > MAX_USER_ID {
return Ok(HttpResponse::BadRequest().body("invalid user_id"));
}
// Limit action length to prevent unbounded allocations.
const MAX_ACTION_LEN: usize = 256;
if payload.action.len() > MAX_ACTION_LEN {
return Ok(HttpResponse::PayloadTooLarge().body("action too long"));
}
let key = b"another-secret-key-32-bytes-long-abc123";
let mut mac = HmacSha256::new_from_slice(key)
.map_err(|_| HttpResponse::InternalServerError().body("hmac init error"))?;
// Canonical representation: serialize only the signed fields.
let serialized = serde_json::to_string(&(&payload.user_id, &payload.action))
.map_err(|_| HttpResponse::InternalServerError().body("serialization error"))?;
mac.update(serialized.as_bytes());
// In a real service, you would receive the MAC from the request and verify.
// For demonstration we compute and return it.
let result = mac.finalize();
let code = hex::encode(result.into_bytes());
Ok(HttpResponse::Ok().json(serde_json::json!({ "hmac": code })))
}
Key remediation practices derived from these patterns:
- Validate size and type of all inputs before they reach HMAC computation.
- Use bounded buffers and avoid concatenating uncontrolled data into the signed payload.
- Apply constant‑time comparison for MAC verification to prevent timing attacks.
- Leverage Serde to explicitly control which fields are included in the signature.
These measures reduce the likelihood that untrusted data can influence heap allocations or indexing in ways that expose a heap overflow or memory corruption path when HMAC signatures are used in Actix services.