Fix Command Injection in Actix Web
Command injection in Actix Web occurs when user-controlled data is passed directly to system shells or process builders without proper sanitization or parameterization. In Rust, utilizing `std::process::Command` with `sh -c` or similar shell wrappers is the primary vector for this vulnerability, allowing attackers to break out of the intended command context using shell metacharacters like `;`, `&`, `|`, or backticks.
The Vulnerable Pattern
use actix_web::{web, HttpResponse, Responder}; use std::process::Command; use serde::Deserialize;#[derive(Deserialize)] pub struct Params { target: String, }
async fn check_health(info: web::Query
) -> impl Responder { // DANGER: User input is concatenated into a shell command string let output = Command::new(“sh”) .arg(“-c”) .arg(format!(“curl -I {}”, info.target)) .output(); match output { Ok(out) => HttpResponse::Ok().body(String::from_utf8_lossy(&out.stdout).to_string()), Err(_) => HttpResponse::InternalServerError().finish(), }
}
The Secure Implementation
The fix implements a defense-in-depth strategy. First, it removes the shell invocation (`sh -c`). By calling the binary (`curl`) directly and passing arguments via `.arg()`, the OS treats the input as a literal string for the process rather than interpreting it for shell tokens. Second, it adds strict regex validation to ensure the input matches an expected pattern (e.g., a hostname), preventing any unexpected characters from reaching the process execution layer.
use actix_web::{web, HttpResponse, Responder}; use std::process::Command; use serde::Deserialize; use regex::Regex;#[derive(Deserialize)] pub struct Params { target: String, }
async fn check_health_secure(info: web::Query
) -> impl Responder { // 1. Strict Input Validation (Allowlist/Regex) let re = Regex::new(r”^[a-zA-Z0-9.-]+$“).unwrap(); if !re.is_match(&info.target) { return HttpResponse::BadRequest().body(“Invalid target format”); } // 2. Parameterization: Pass arguments directly to the binary, bypassing the shell let output = Command::new("curl") .arg("-I") .arg(&info.target) // This is treated as a literal argument, not a shell command .output(); match output { Ok(out) => HttpResponse::Ok().body(String::from_utf8_lossy(&out.stdout).to_string()), Err(_) => HttpResponse::InternalServerError().finish(), }
}
Your Actix Web API
might be exposed to Command Injection
74% of Actix Web apps fail this check. Hackers use automated scanners to find this specific flaw. Check your codebase before they do.
Free Tier • No Credit Card • Instant Report
Verified by Ghost Labs Security Team
This content is continuously validated by our automated security engine and reviewed by our research team. Ghost Labs analyzes over 500+ vulnerability patterns across 40+ frameworks to provide up-to-date remediation strategies.