HIGH command injectionaxumbasic auth

Command Injection in Axum with Basic Auth

Command Injection in Axum with Basic Auth — how this specific combination creates or exposes the vulnerability

Command injection occurs when untrusted input is concatenated into a system command, allowing an attacker to execute arbitrary shell commands. In Axum, this risk arises when request data (e.g., headers, query parameters, or body fields) is passed to a command executor such as std::process::Command. The use of HTTP Basic Auth can inadvertently amplify the exposure: applications often log authentication headers or embed credentials in diagnostic contexts, and if any of that data is forwarded into shell commands, it expands the attack surface.

Consider an endpoint that accepts a username and password via Basic Auth and also takes a query parameter filename to generate a report. A vulnerable implementation might construct a shell command using string interpolation:

// WARNING: This is an insecure example for educational purposes only
use axum::{routing::get, Router};
use std::process::Command;

async fn report_handler(
    // filename from query; user/pass from Basic Auth headers
    filename: String,
) -> String {
    // UNSAFE: direct concatenation of user input into a shell command
    let output = Command::new("sh")
        .arg("-c")
        .arg(format!("echo generating report for {} && cat /data/{}", filename, filename))
        .output()
        .expect("failed to execute command");
    String::from_utf8_lossy(&output.stdout).to_string()
}

let app = Router::new().route("/report", get(report_handler));

An attacker who knows the endpoint can provide filename as '; id #. Even if the application uses Basic Auth to identify the caller, the command runs with the process’s permissions. Because the command is built by concatenating the user-controlled filename, the attacker can break out of the intended argument and execute id (or any other shell command). The Basic Auth credentials themselves are not used in the command, but if logs or error messages include the Authorization header, they may aid the attacker in crafting subsequent injections or correlating requests.

In a real-world scenario, this pattern can lead to more severe outcomes: reading sensitive files, modifying data, or pivoting within the network. The risk is especially pronounced when the application also supports OpenAPI/Swagger spec analysis, because an exposed endpoint with unchecked input can be discovered automatically by scanners that correlate spec definitions with runtime behavior. middleBrick, for example, would flag such command injection findings under Input Validation and BFLA/Privilege Escalation checks, providing severity and remediation guidance without attempting to fix the code.

Basic Auth-Specific Remediation in Axum — concrete code fixes

Remediation focuses on two pillars: never pass untrusted input directly to a shell, and handle credentials safely without leaking them into logs or commands. For Axum services, prefer structured data processing over shell invocation; if a command must be executed, use a language-native library that avoids a shell entirely, and treat Basic Auth credentials as opaque tokens rather than embedding them in command arguments.

1. Avoid the shell; use direct command execution. Replace shell-based commands with explicit binaries and pass arguments as a vector, bypassing shell parsing and metacharacters:

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

async fn safe_report_handler(
    // filename from query; user credentials handled separately by Axum middleware
    filename: String,
) -> Json {
    // SAFE: no shell involved; arguments are passed directly
    let output = Command::new("/usr/bin/cat")
        .arg("/data")
        .arg(&filename)
        .output()
        .expect("failed to execute cat");
    Json(String::from_utf8_lossy(&output.stdout).to_string())
}

This eliminates injection via shell metacharacters such as ;, &, or |. Ensure the path and arguments are validated (e.g., restrict to known-safe filenames) and avoid using user input as part of the binary path itself.

2. Handle Basic Auth securely and separately. Use middleware to parse credentials, store them only as necessary in a secure runtime context, and avoid logging them. Do not include Authorization headers in command construction or diagnostic output:

use axum::{async_trait, extract::Request, middleware::Next, response::Response, http::header};
use std::convert::Infallible;

// Example middleware that extracts Basic Auth credentials without leaking them
pub struct BasicAuth;

#[async_trait]
impl tower_layer::Layer<S> for BasicAuth {
    type Service = AuthMiddleware<S>;
    fn layer(&self, inner: S) -> Self::Service {
        AuthMiddleware { inner }
    }
}

pub struct AuthMiddleware<S> {
    inner: S,
}

#[async_trait]
impl<S> tower_service::Service<Request> for AuthMiddleware<S>
where
    S: tower_service::Service<Request, Response = Response, Error = Infallible> + Send + 'static,
    S::Future: Send + 'static,
{
    type Response = S::Response;
    type Error = S::Error;
    type Future = std::future::Ready<Result<Self::Response, Self::Error>>;

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

    fn call(&self, req: Request) -> Self::Future {
        // Extract and optionally validate credentials, but do not forward to commands or logs
        if let Some(auth_header) = req.headers().get(header::AUTHORIZATION) {
            // Validate format "Basic base64(credentials)" without decoding or storing unnecessarily
            let _ = auth_header.to_str().map(|s| s.starts_with("Basic "));
        }
        self.inner.call(req)
    }
}

With these practices, the API remains functional while reducing the likelihood that user-controlled data reaches a shell. middleBrick’s scans (via CLI, GitHub Action, or MCP Server integrations) can verify that such mitigations are in place by checking for unsafe command construction patterns in the exposed surface.

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

Can an attacker exploit Basic Auth headers to cause command injection in Axum?
Not directly if headers are not used in command construction. The risk arises when any user-controlled data—including query parameters or body fields—is concatenated into shell commands. Basic Auth credentials should be handled by middleware and never embedded into command arguments or logs to avoid aiding injection attempts.
Does using Basic Auth in Axum require extra steps to prevent command injection?
Yes. Beyond standard input validation, ensure that authentication data is isolated from command execution paths. Use structured, shell-free command invocation and avoid logging or exposing Authorization headers. middleBrick can scan your endpoints to confirm unsafe patterns are absent, providing findings and remediation guidance.