HIGH path traversalaxum

Path Traversal in Axum

How Path Traversal Manifests in Axum

Path traversal vulnerabilities in Axum typically arise when user-controlled input is used to construct file system paths without proper validation. Axum's routing and extraction mechanisms can inadvertently expose this risk when handlers use Path, Query, or raw Request parts to build paths for std::fs operations. A common pattern involves extracting a filename parameter and directly appending it to a base directory.

For example, an endpoint designed to serve user uploads might look like this:

use axum::extract::Path;
use std::fs;

async fn serve_file(Path(filename): Path) -> Result {
    let path = format!("./uploads/{}", filename);
    fs::File::open(path).map_err(|e| {
        (axum::http::StatusCode::NOT_FOUND, e.to_string())
    })
}

// Route: axum::Router::new().route("/files/*filename", get(serve_file))

If filename contains ../ sequences (e.g., ../../etc/passwd), the constructed path escapes the intended ./uploads directory. Axum's Path extractor does not decode or sanitize such sequences by default, passing them directly to the handler. This differs from frameworks that automatically normalize paths; Axum gives developers full control, which requires vigilant input handling.

Another vector occurs with query parameters:

use axum::extract::Query;
use serde::Deserialize;

#[derive(Deserialize)]
struct FileParams {
    name: String,
}

async fn serve_file_query(Query(params): Query) -> Result {
    let path = format!("./data/{}", params.name);
    fs::read_to_string(path).map_err(|e| {
        (axum::http::StatusCode::BAD_REQUEST, e.to_string())
    })
}

Here, params.name is user-controlled and unsanitized. An attacker could submit ?name=../../../etc/shadow to read sensitive files. These patterns are especially dangerous in Axum applications serving static assets, user-generated content, or configuration files where the base directory is assumed to be safe.

Axum-Specific Detection

Detecting path traversal in Axum requires analyzing how user input flows into file system operations. middleBrick identifies these issues through black-box testing by probing parameters with traversal sequences (../, ..\, URL-encoded variants) and monitoring for abnormal responses (e.g., successful file access, error messages revealing internal paths). Since middleBrick scans the unauthenticated attack surface, it tests endpoints without needing credentials, focusing on where input is reflected in file access.

For the Axum examples above, middleBrick would:

  • Send requests like GET /files/%2e%2e%2f%2e%2e%2fetc%2fpasswd (URL-encoded ../../etc/passwd) to test the Path-based endpoint.
  • Probe GET /serve?name=%2e%2e%2f%2e%2e%2fetc%2fpasswd for the query-based variant.
  • Check responses for signs of success (e.g., 200 OK with file contents) or error messages that leak path information (e.g., No such file or directory: ./uploads/../../etc/passwd).
  • Test encoded and double-encoded bypass attempts (e.g., ..%252f) to catch incomplete decoding logic.

middleBrick’s scanning includes 12 parallel checks, with path traversal falling under Property Authorization and Input Validation categories. It does not require source code or configuration—only the API URL. When a vulnerability is detected, middleBrick reports it with severity (typically High for file read, Critical for write/delete), location (endpoint and parameter), and remediation guidance tailored to the finding.

For Axum developers, integrating middleBrick into CI/CD via the GitHub Action allows automatic scanning on pull requests. The action can be configured to fail builds if the security score drops below a threshold (e.g., grade C), catching regressions early. Example workflow:

name: API Security Scan
on: [pull_request]
jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run middleBrick scan
        uses: middlebrick/github-action@v1
        with:
          api-url: https://staging.example.com
          fail-below: C

This ensures that path traversal and other issues are caught before deployment, leveraging middleBrick’s ability to test the live unauthenticated surface without agents or credentials.

Axum-Specific Remediation

Fixing path traversal in Axum centers on validating and sanitizing user input before it interacts with the file system. Axum does not provide built-in path sanitization, so developers must use Rust’s standard library or trusted crates to neutralize traversal attempts. The key is to ensure the resolved path remains within an intended base directory.

For the Path-based endpoint, use std::path::Path to join and canonicalize paths, then verify the result starts with the base directory:

use axum::extract::Path;
use std::fs;
use std::path::{Path, PathBuf};

async fn serve_file_safe(Path(filename): Path) -> Result {
    let base_dir = Path::new("./uploads");
    let requested_path = base_dir.join(&filename);
    
    // Normalize path (removes `..` and `.` components)
    let normalized_path = match requested_path.canonicalize() {
        Ok(p) => p,
        Err(_) => return Err((axum::http::StatusCode::BAD_REQUEST, "Invalid path".into())),
    };
    
    // Ensure the normalized path is still within base_dir
    if !normalized_path.starts_with(base_dir) {
        return Err((axum::http::StatusCode::FORBIDDEN, "Path traversal attempt".into()));
    }
    
    fs::File::open(normalized_path).map_err(|e| {
        (axum::http::StatusCode::NOT_FOUND, e.to_string())
    })
}

This approach uses canonicalize() to resolve the absolute path, eliminating ../ and ./ sequences. The starts_with check confirms the file is inside ./uploads. Note that canonicalize() fails if the path does not exist, which is acceptable for read-only endpoints (it prevents information leakage about non-existent paths). For endpoints that create files, check existence before canonicalizing or use fs::metadata separately.

For query-based endpoints, apply the same logic:

use axum::extract::Query;
use serde::Deserialize;
use std::fs;
use std::path::{Path, PathBuf};

#[derive(Deserialize)]
struct FileParams {
    name: String,
}

async fn serve_file_query_safe(Query(params): Query) -> Result {
    let base_dir = Path::new("./data");
    let requested_path = base_dir.join(¶ms.name);
    
    let normalized_path = match requested_path.canonicalize() {
        Ok(p) => p,
        Err(_) => return Err((axum::http::StatusCode::BAD_REQUEST, "Invalid path".into())),
    };
    
    if !normalized_path.starts_with(base_dir) {
        return Err((axum::http::StatusCode::FORBIDDEN, "Path traversal attempt".into()));
    }
    
    fs::read_to_string(normalized_path)
        .map_err(|e| (axum::http::StatusCode::NOT_FOUND, e.to_string()))
}

Alternative: Use Path::strip_prefix after normalization to avoid starts_with (which can have edge cases with symlinks). However, starts_with is safe when combined with canonicalize() as shown.

For serving static files, consider Axum’s ServeDir from tower-http, which handles traversal internally:

use tower_http::services::ServeDir;
use axum::Router;

let app = Router::new().nest_service("/files", ServeDir::new("./uploads"));

ServeDir securely serves files from a directory, blocking traversal attempts by default. This is often safer than manual implementation for static asset serving.

After fixing, rescan with middleBrick to confirm the vulnerability is resolved. The tool will update the security score and findings, providing validation that the remediation effective against the unauthenticated attack 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

Does Axum automatically prevent path traversal in its routing or extractors?
No, Axum’s routing and extractors like Path and Query pass user input directly to handlers without decoding or sanitizing path traversal sequences (e.g., ../). Developers must manually validate that constructed file paths remain within an intended base directory using methods like canonicalize() and starts_with checks.
Can middleBrick detect path traversal in Axum APIs that require authentication?
middleBrick scans the unauthenticated attack surface only. It does not send credentials, headers, or tokens unless they are part of the public URL (e.g., query parameters). For authenticated endpoints, you must first scan them in an unauthenticated state (if possible) or use the tool in a staging environment where authentication is temporarily disabled for scanning—though this is not recommended for production-like validation of auth logic.