HIGH command injectionaxummutual tls

Command Injection in Axum with Mutual Tls

Command Injection in Axum with Mutual Tls — how this specific combination creates or exposes the vulnerability

Command Injection occurs when an attacker can inject and execute arbitrary system commands through an application. In Axum, this often arises when user-controlled input is passed to a shell or a command executor without proper validation or sanitization. When Mutual Transport Layer Security (Mutual Tls) is used, the focus shifts to how identity and authorization are enforced at the application layer after the TLS handshake completes. While Mutual Tls ensures that both client and server present valid certificates, it does not inherently protect against malicious payloads embedded within authenticated requests.

Consider a scenario where an authenticated client presents a certificate, and the server extracts a field such as a Common Name (CN) or a custom certificate extension to use in constructing system commands. If this data is concatenated into a shell command without escaping, an attacker with a valid certificate could inject shell metacharacters. For example, a CN value like test; cat /etc/passwd could lead to unintended command execution. This is especially dangerous in Axum handlers that invoke external utilities for administrative or diagnostic tasks, as the trust boundary is incorrectly assumed to be at the TLS layer rather than at the input validation layer.

Even with Mutual Tls, Axum applications often rely on runtime parameters, headers, or path segments that may be derived from certificate attributes. If those attributes are not treated as untrusted input, the combination of Mutual Tls and dynamic command construction creates a blind spot. Security scans using tools that test the unauthenticated attack surface can detect such command injection vectors by probing endpoints that execute shell commands, regardless of the TLS configuration. The key takeaway is that Mutual Tls provides channel integrity and peer authentication, but it does not sanitize data that flows through the application logic.

Mutual Tls-Specific Remediation in Axum — concrete code fixes

Remediation focuses on strict input validation, avoiding shell invocation, and ensuring that certificate-derived data is never directly concatenated into commands. Below are concrete Axum examples demonstrating secure patterns.

1. Avoid shell execution entirely by using structured commands

Instead of invoking a shell, use Rust's std::process::Command with explicit arguments. This prevents the shell from interpreting metacharacters.

use axum::{routing::get, Router};
use std::process::Command;

async fn handle_user_info(username: String) -> String {
    // Safe: pass arguments directly without shell
    let output = Command::new("id")
        .arg(&username)
        .output()
        .expect("failed to execute command");
    String::from_utf8_lossy(&output.stdout).to_string()
}

fn app() -> Router {
    Router::new()
        .route("/user/:username", get(|username: String| async move {
            handle_user_info(username).await
        }))
}

2. Validate and sanitize certificate-derived inputs

If you must use data from client certificates (e.g., via request extensions), treat it as untrusted. Use allowlists and reject any input containing shell metacharacters.

use axum::{routing::post, Extension, Json};
use serde::{Deserialize, Serialize};

#[derive(Deserialize)]
struct CommandRequest {
    action: String,
}

fn is_valid_input(s: &str) -> bool {
    // Allow only alphanumeric and underscores
    s.chars().all(|c| c.is_alphanumeric() || c == '_')
}

async fn run_action(Json(payload): Json) -> String {
    if !is_valid_input(&payload.action) {
        return String::from("invalid input");
    }
    // Safe execution with validated input
    let output = std::process::Command::new("logger")
        .arg(&payload.action)
        .output()
        .unwrap();
    format!("logged: {}", String::from_utf8_lossy(&output.stdout))
}

fn app() -> axum::Router {
    axum::Router::new()
        .route("/run", post(run_action))
}

3. Enforce Mutual Tls and inspect certificate fields safely

When using tower-rs or similar middleware to enforce Mutual Tls, extract certificate fields and validate them before any use. Do not pass them directly to shell commands.

use axum::{async_trait, extract::Request, middleware, Router};
use std::convert::Infallible;
use tls_parser::Certificate;

struct CertValidator;

#[async_trait]
impl tower::Layer for CertValidator {
    type Service = ValidatedService;

    fn layer(&self, inner: S) -> Self::Service {
        ValidatedService { inner }
    }
}

struct ValidatedService {
    inner: S,
}

impl tower::Service> for ValidatedService
where
    S: tower::Service, Response = axum::response::Response, Error = Infallible> + Clone + Send + 'static,
    S::Future: Send + 'static,
    B: Send + 'static,
{
    type Response = S::Response;
    type Error = S::Error;
    type Future = std::future::Ready>;

    fn poll_ready(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll> {
        std::task::Poll::Ready(Ok(()))
    }

    fn call(&mut self, mut req: Request) -> Self::Future {
        // Example: inspect certificate extension for allowed values
        if let Some(cert) = req.extensions().get::() {
            let subject = cert.subject();
            let cn = subject.common_name().unwrap_or_default();
            if cn.contains(';') || cn.contains('&') || cn.contains('|') {
                let response = axum::response::Response::builder()
                    .status(403)
                    .body("Forbidden".into())
                    .unwrap();
                return std::future::ready(Ok(response));
            }
        }
        let fut = self.inner.call(req);
        std::future::ready(fut.map(|res| res.map_err(|err| err.into())))
    }
}

fn app() -> Router {
    Router::new()
        .route_safe("/admin", get(|| async { "ok" }))
        .layer(CertValidator)
}

Related CWEs: inputValidation

CWE IDNameSeverity
CWE-20Improper Input Validation HIGH
CWE-22Path Traversal HIGH
CWE-74Injection CRITICAL
CWE-77Command Injection CRITICAL
CWE-78OS Command Injection CRITICAL
CWE-79Cross-site Scripting (XSS) HIGH
CWE-89SQL Injection CRITICAL
CWE-90LDAP Injection HIGH
CWE-91XML Injection HIGH
CWE-94Code Injection CRITICAL

Frequently Asked Questions

Does Mutual Tls prevent command injection in Axum?
No. Mutual Tls ensures client authentication and channel encryption, but it does not sanitize user or certificate-derived inputs. Command injection depends on how input is handled, not on transport-layer authentication.
What is the best practice for using certificate data in Axum command execution?
Treat certificate fields as untrusted input. Validate against an allowlist, avoid shell invocation by using structured commands, and never concatenate raw certificate data into shell commands.