Formula Injection in Actix with Hmac Signatures
Formula Injection in Actix with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Formula Injection occurs when user-controlled data is incorporated into evaluation contexts such as formulas, expressions, or executable code. In Actix web applications, combining dynamic data with Hmac signatures can introduce risks when signature verification is implemented inconsistently or when the signature itself is misused as a trust boundary.
Consider an endpoint that accepts query parameters used to build a business logic expression (for example, a calculation or a dynamic filter). If the server embeds these parameters into a string that is later hashed to produce or verify an Hmac signature, and then uses the resulting signature to authorize an action without re-validating the original inputs, an attacker may supply formula-like payloads that alter the computed signature or bypass expected validation paths. A typical vulnerable pattern is building a signing string from unchecked user inputs:
use actix_web::{web, HttpResponse};
use hmac::{Hmac, Mac};
use sha2::Sha256;
type HmacSha256 = Hmac<Sha256>;
async fn calculate(query: web::Query<serde_json::Value>) -> HttpResponse {
let input = query.get("value").and_then(|v| v.as_str()).unwrap_or("");
let key = b"super-secret-key";
let mut mac = HmacSha256::new_from_slice(key).expect("HMAC can take key of any size");
mac.update(input.as_bytes());
let result = mac.finalize();
let signature = result.into_bytes();
// Further logic that may treat `signature` as proof of correctness
HttpResponse::Ok().body(format!("Signature: {:x}", signature))
}
If input contains characters or sequences that affect how the signing string is interpreted downstream (for example, used in a later evaluator or a stored procedure that parses formulas), the Hmac does not protect against injection in the evaluated expression itself. The signature only verifies that the value matched at signing time; it does not sanitize or validate the semantic safety of the content. An attacker might supply payloads such as 1+2*3 or encoded references that, when processed by a downstream formula engine, trigger unintended behavior. Because the Actix handler treats the Hmac as a completeness check rather than part of a broader validation strategy, the application may execute or transform attacker-controlled expressions.
Another variant involves path or header-based signing where the signature is computed over a subset of headers or URL components that later influence routing or data access decisions. If the application uses the signature to authorize access without re-checking the underlying data constraints, an attacker can craft inputs that result in a valid signature for one context but an unexpected interpretation in another. For example, an unsigned parameter that influences resource selection can be combined with a signed parameter, and the server may incorrectly trust the combination because the signature appears valid.
These patterns illustrate Formula Injection in the context of Actix with Hmac Signatures: the signature operation does not prevent malicious data from being interpreted as code or instructions by downstream components. The vulnerability is not in the Hmac algorithm itself, but in the application logic that allows unchecked data to reach formula-evaluating layers despite the presence of a signature.
Hmac Signatures-Specific Remediation in Actix — concrete code fixes
Remediation centers on ensuring that Hmac signatures are used only for integrity verification of exact, canonical inputs and never as a substitute for input validation, output encoding, or business logic safeguards. In Actix, you should validate and sanitize all user-controlled data before including it in the signed string, and avoid using the signature to authorize indirectly evaluated expressions.
First, strictly define the signing scope. Only include data that is necessary and safe, and canonicalize it (e.g., trim, enforce type conversions, and reject unexpected keys). Do not embed user input directly into arithmetic or script-like expressions before signing:
use actix_web::{web, HttpResponse};
use hmac::{Hmac, Mac};
use sha2::Sha256;
use serde::{Deserialize, Serialize};
type HmacSha256 = Hmac<Sha256>;
#[derive(Deserialize, Serialize)]
struct SignedRequest {
amount: u64,
currency: String,
nonce: String,
}
async fn verify_payment(form: web::Form<SignedRequest>) -> HttpResponse {
let key = b"super-secret-key";
let mut mac = HmacSha256::new_from_slice(key).expect("HMAC can take key of any size");
// Canonical representation: fixed fields, no extra whitespace, stable ordering
mac.update(b"amount=");
mac.update(form.amount.to_string().as_bytes());
mac.update(b";currency=");
mac.update(form.currency.as_bytes());
mac.update(b";nonce=");
mac.update(form.nonce.as_bytes());
match mac.verify_slice(&form.signature) {
Ok(_) => {
// Proceed with business logic using validated, typed values
HttpResponse::Ok().body("Payment verified")
}
Err(_) => HttpResponse::BadRequest().body("Invalid signature"),
}
}
Second, reject inputs that contain characters or patterns relevant to formula evaluation (such as operators, script tags, or template syntax) unless they are explicitly required and safely encoded. Use allowlists for known-good values and treat the Hmac as a last-line integrity check, not a primary validation mechanism:
use actix_web::{web, HttpResponse};
use hmac::{Hmac, Mac};
use sha2::Sha256;
fn is_safe_value(value: &str) -> bool {
// Allow only alphanumeric and a few safe characters
value.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
}
async fn safe_compute(query: web::Query<serde_json::Value>) -> HttpResponse {
let raw = query.get("data").and_then(|v| v.as_str()).unwrap_or("");
if !is_safe_value(raw) {
return HttpResponse::BadRequest().body("Invalid input");
}
let key = b"super-secret-key";
let mut mac = HmacSha256::new_from_slice(key).expect("HMAC can take key of any size");
mac.update(raw.as_bytes());
let signature = mac.finalize();
HttpResponse::Ok().body(format!("Signature: {:x}", signature.into_bytes()))
}
Third, when signatures are used in API authentication or routing, re-validate all constraints on the server side rather than relying on the signature alone. For example, if a signature indicates that a request is authorized to access a resource, also verify that the resource ID conforms to expected patterns and that the requesting context matches additional policies:
use actix_web::{web, HttpResponse, HttpRequest};
use hmac::{Hmac, Mac};
use sha2::Sha256;
async fn handle_signed_route(
req: HttpRequest,
query: web::Query<serde_json::Value>,
) -> HttpResponse {
let provided_id = query.get("resource_id").and_then(|v| v.as_str()).unwrap_or("");
// Validate ID format independently of signature
if !provided_id.chars().all(|c| c.is_ascii_hexdigit()) {
return HttpResponse::BadRequest().body("Invalid resource ID");
}
let key = b"super-secret-key";
let mut mac = HmacSha256::new_from_slice(key).expect("HMAC can take key of any size");
mac.update(provided_id.as_bytes());
// signature from header or query
let sig = match req.headers().get("X-Signature") {
Some(h) => h.to_str().unwrap_or(""),
None => return HttpResponse::Unauthorized().body("Missing signature"),
};
// In real code, use constant-time comparison
if format!("{:x}", mac.finalize()) == sig {
HttpResponse::Ok().body("Access granted")
} else {
HttpResponse::Unauthorized().body("Invalid signature")
}
}