Command Injection in Actix with Hmac Signatures
Command Injection in Actix with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Command injection occurs when an API passes untrusted data directly to a system shell or to executable logic without validation or sanitization. In Actix-based services that use Hmac Signatures to authenticate requests, a common pattern is to include a signature in query parameters or headers that is derived from a secret key and selected request attributes. If the application uses those same attributes to construct shell commands or dynamic arguments, an attacker can manipulate the signed values to inject additional shell commands.
Consider an endpoint that accepts filename and action parameters, signs them with Hmac, and then passes them to a system utility such as tar or grep. Even though the Hmac verifies integrity and origin, the signature does not protect against logic abuse: the server must still treat the input as untrusted. If the server builds a command string by concatenation, an attacker can supply values like file.txt; cat /etc/passwd or use quoting/escaping to break argument boundaries. Because the Hmac is computed over the attacker-controlled values, the server will accept the tampered payload as valid.
In Actix web, this often appears in handlers that use web::query to extract parameters and then invoke system operations via libraries or OS utilities. The vulnerability is not in Hmac itself, but in how the application consumes the validated data. The risk is higher when the endpoint exposes functionality such as file archiving, log inspection, or custom tooling invoked via shell commands. Attack techniques resemble standard OS command injection, for example using semicolons, pipes, or environment variable expansion to execute unintended code.
An additional subtlety is path or argument traversal combined with command injection: an attacker may attempt values like ../../../etc/passwd | base64 to both read files and execute shell code if the server decodes or pipes the input. Because Hmac Signatures do not constrain the semantics of parameter values, developers must enforce strict allowlists on command arguments and avoid building commands from raw user input, even when the request is cryptographically signed.
Real-world patterns to look for include usage of std::process::Command with interpolated strings, or calls to external utilities where the command or its arguments contain unchecked data. The presence of Hmac Signatures may give a false sense of security; the signature ensures the request has not been altered in transit, but it does not enforce business-rule validation or safe process invocation.
Hmac Signatures-Specific Remediation in Actix — concrete code fixes
Remediation focuses on strict input validation, avoiding shell construction, and using safe process invocation. Do not rely on Hmac Signatures alone to make user input safe. Implement allowlists for known-safe values, and use structured APIs instead of shell commands where possible.
Example of a vulnerable handler in Actix that builds a shell command from signed query parameters:
// Vulnerable example: do not use
use actix_web::{get, web, App, HttpResponse, HttpServer, Responder};
use hmac::{Hmac, Mac};
use sha2::Sha256;
type HmacSha256 = Hmac<Sha256>;
#[get("/run")]
async fn run_command(query: web::Query<HashMap<String, String>>) -> impl Responder {
let key = b"super-secret-key";
let mut mac = HmacSha256::new_from_slice(key).expect("Hmac can take key of any size");
mac.update(query["data"].as_bytes());
let result = mac.finalize();
let computed = result.into_bytes();
// naive verification omitted for brevity
let cmd = format!("echo {}", query.get("data").unwrap_or(""));
let output = std::process::Command::new("/bin/sh").arg("-c").arg(cmd).output().unwrap();
HttpResponse::Ok().body(String::from_utf8_lossy(&output.stdout).to_string())
}
Secure alternative using strict allowlist and avoiding shell invocation:
use actix_web::{get, web, App, HttpResponse, HttpServer, Responder};
use std::collections::HashMap;
use hmac::{Hmac, Mac};
use sha2::Sha256;
type HmacSha256 = Hmac<Sha256>;
#[get("/run")]
async fn run_command(
query: web::Query<HashMap<String, String>>,
) -> impl Responder {
let key = b"super-secret-key";
let mut mac = HmacSha256::new_from_slice(key).expect("Hmac can take key of any size");
mac.update(query["data"].as_bytes());
let result = mac.finalize();
// In real code, perform constant-time comparison of the signature
// if !verify_signature(...) { return HttpResponse::BadRequest().finish(); }
// Strict allowlist: only permit known-safe values
let allowed = ["list", "info", "hash"];
let action = query.get("action").map(|s| s.as_str()).unwrap_or("");
if !allowed.contains(&action) {
return HttpResponse::BadRequest().body("Invalid action");
}
// Safe, structured invocation without shell
let output = match action {
"list" => std::process::Command::new("ls").arg("-l").output(),
"info" => std::process::Command::new("uname").arg("-a").output(),
"hash" => {
let mut hasher = sha2::Sha256::new();
hasher.update(query["data"].as_bytes());
let result = hasher.finalize();
format!("{:x}", result)
},
_ => unreachable!(),
};
match output {
Ok(o) => HttpResponse::Ok().body(String::from_utf8_lossy(&o.stdout).to_string()),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}
Key remediation practices:
- Never concatenate user input into shell commands; use
std::process::Commandwith separate arguments. - Enforce strict allowlists on enumerated actions rather than passing raw input to the command layer.
- Validate Hmac signatures using constant-time comparison to prevent timing attacks.
- Avoid invoking a shell (
/bin/sh -c) unless absolutely necessary; if required, rigorously sanitize and quote all inputs. - Apply principle of least privilege to the runtime environment to limit impact if injection does occur.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |