GuardAPI Logo
GuardAPI

Fix Command Injection in Axum

Command injection in Axum occurs when user-controlled input is concatenated into system commands and executed via a shell. In Rust, 'std::process::Command' is safe by default as it doesn't invoke a shell, but developers often break this security boundary by explicitly calling 'sh -c' or 'cmd.exe /c' to leverage shell features. This allows attackers to chain commands using characters like ';', '&', or '|'.

The Vulnerable Pattern

use axum::{extract::Query, response::IntoResponse};
use std::collections::HashMap;
use std::process::Command;

async fn handler(Query(params): Query<HashMap<String, String>>) -> impl IntoResponse { let user_input = params.get(“filename”).unwrap(); // VULNERABLE: Using ‘sh -c’ with string interpolation allows command chaining let output = Command::new(“sh”) .arg(“-c”) .arg(format!(“ls -l contents/{}”, user_input)) .output() .expect(“failed to execute process”);

String::from_utf8_lossy(&output.stdout).to_string()

}

The Secure Implementation

The fix eliminates the shell intermediary. By calling 'Command::new("ls")' directly instead of 'sh -c', the OS treats the user input as a literal argument vector rather than a string to be parsed for shell metacharacters. Even if an attacker passes '; rm -rf /', the system will simply look for a file named '; rm -rf /' rather than executing the command. As a secondary defense, always validate input against an allowlist or use Rust's filesystem APIs instead of external binaries where possible.

use ax_um::{extract::Query, response::IntoResponse, http::StatusCode};
use std::collections::HashMap;
use std::process::Command;

async fn handler(Query(params): Query<HashMap<String, String>>) -> impl IntoResponse { let user_input = match params.get(“filename”) { Some(val) => val, None => return (StatusCode::BAD_REQUEST, “Missing param”).into_response(), };

// SECURE: 1. Avoid the shell wrapper. Call the binary directly.
// SECURE: 2. Input is passed as a literal argument, not parsed by a shell.
let output = Command::new("ls")
    .arg("-l")
    .arg(format!("contents/{}", user_input))
    .output();

match output {
    Ok(o) => String::from_utf8_lossy(&o.stdout).to_string().into_response(),
    Err(_) => (StatusCode::INTERNAL_SERVER_ERROR, "Execution failed").into_response(),
}

}

System Alert • ID: 2422
Target: Axum API
Potential Vulnerability

Your Axum API might be exposed to Command Injection

74% of Axum apps fail this check. Hackers use automated scanners to find this specific flaw. Check your codebase before they do.

RUN FREE SECURITY DIAGNOSTIC
GuardLabs Engine: ONLINE

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.