HIGH zip slipaxummutual tls

Zip Slip in Axum with Mutual Tls

Zip Slip in Axum with Mutual Tls — how this specific combination creates or exposes the vulnerability

Zip Slip is a path traversal vulnerability where an attacker-controlled filename in an archive leads to writing files outside the intended directory. In Axum, a Rust web framework, this typically arises when extracting uploaded archives without validating that resolved paths remain within a designated base directory. Adding mutual Transport Layer Security (mTLS) changes the trust boundary but does not remove path traversal risks; it only ensures that the client presenting a certificate is authenticated and authorized at the TLS layer. With mTLS enabled, an authenticated client may still send a malicious archive containing crafted paths, and if the server-side extraction logic does not sanitize .. segments or uses concatenation without canonicalization, files can escape the target folder. The combination therefore exposes the vulnerability because authentication via certificates does not equate to input validation; the server must still validate and sanitize filenames and archive entries independently of TLS client identity.

Consider an Axum handler that accepts a client certificate and then extracts a ZIP uploaded by the authenticated client. If the handler uses a library that resolves paths by simple string joining, an entry named ../../../etc/passwd can escape the extraction root. mTLS ensures the request comes from a trusted client, but it does not prevent the server from processing malicious payloads. This is especially relevant when the API accepts file uploads as part of a workflow where authenticated clients are expected to submit archives. The risk is not introduced by mTLS, but mTLS can increase the attack surface by enabling more endpoints to accept authenticated uploads, and it may reduce suspicion around traffic that appears cryptographically verified.

In the context of middleBrick’s checks, this scenario would be flagged under Input Validation and BOLA/IDOR considerations: the server must verify path resolution for each entry and enforce strict containment. MiddleBrick’s scans test the unauthenticated attack surface, but when mTLS is required, testing must be performed with a valid client certificate to accurately assess extraction logic. MiddleBrick’s OpenAPI/Swagger analysis can detect endpoints accepting file uploads and highlight missing path sanitization where $ref definitions indicate binary or archive payloads.

Mutual Tls-Specific Remediation in Axum — concrete code fixes

To remediate Zip Slip in Axum while using mutual TLS, focus on secure archive extraction regardless of TLS client identity. Validate and sanitize every entry path before joining it to the destination directory. Use canonicalization and ensure the resolved path remains within the intended base directory. Below are concrete code examples demonstrating safe extraction with mTLS-enabled Axum services.

use axum::{
    routing::post,
    Router,
    extract::State,
    http::StatusCode,
};
use std::{net::SocketAddr, path::PathBuf, fs, io::Cursor, path::Path};
use tokio::fs::File;
use tokio_util::compat::TokioAsyncReadCompatExt;
use zip::ZipArchive;
use axum_sessions::extractors::Session;
use axum::extract::Extension;
use std::sync::Arc;

struct AppState {
    upload_root: PathBuf,
}

async fn upload_handler(
    State(state): State>,
    session: Session,
    Extension(cert_fingerprint): Extension, // Assume mTLS client fingerprint extracted
) -> Result {
    // In practice, extract the uploaded file from multipart/form-data
    // This example assumes a field named "archive"
    let archive_data = get_archive_data_from_request(/*...*/);
    let cursor = Cursor::new(archive_data);
    let mut archive = ZipArchive::new(cursor).map_err(|e| (StatusCode::BAD_REQUEST, e.to_string()))?;

    for i in 0..archive.len() {
        let mut file = archive.by_index(i).map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
        let out_path = validate_and_sanitize_path(&state.upload_root, file.name())?;
        if file.name().ends_with('/') {
            fs::create_dir_all(&out_path).map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
        } else {
            if let Some(p) = out_path.parent() {
                fs::create_dir_all(p).map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
            }
            let mut outfile = File::create(&out_path).await.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
            std::io::copy(&mut file, &mut outfile).map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
        }
    }
    Ok(StatusCode::OK)
}

fn validate_and_sanitize_path(base: &Path, supplied: &str) -> Result {
    // Normalize the path: remove leading separators, collapse `..`
    let normalized = supplied.trim_start_matches('/');
    let path = Path::new(normalized);
    // Ensure no components are `..`
    if path.components().any(|c| matches!(c, std::path::Component::ParentDir)) {
        return Err((StatusCode::BAD_REQUEST, "Path traversal not allowed".to_string()));
    }
    let full = base.join(path);
    let full = full.canonicalize().unwrap_or(full);
    if full.starts_with(base) {
        Ok(full)
    } else {
        Err((StatusCode::BAD_REQUEST, "Resolved path escapes base directory".to_string()))
    }
}

#[tokio::main]
async fn main() {
    let upload_root = PathBuf::from("/safe/upload/dir");
    let app_state = Arc::new(AppState { upload_root });
    let app = Router::new()
        .route("/upload", post(upload_handler))
        .layer(Extension("client_cert_fingerprint".to_string())) // placeholder for mTLS extraction
        .with_state(app_state);
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

The example demonstrates explicit path validation before extraction. The validate_and_sanitize_path function rejects entries containing parent directory components and ensures the canonicalized path remains within the base directory. In production, integrate this with Axum extractors for multipart data and use middleware to propagate mTLS client details (such as certificate hashes) into request extensions for audit logging. MiddleBrick’s CLI can scan such endpoints when provided with a valid mTLS-enabled URL, and the GitHub Action can enforce a maximum risk score before merging if file upload handlers are present.

Frequently Asked Questions

Does mutual TLS prevent Zip Slip in Axum APIs?
No. Mutual TLS authenticates the client but does not validate or sanitize file paths within archives. Zip Slip depends on how the server extracts and resolves paths; mTLS must be complemented by strict input validation and path canonicalization.
How can I test Zip Slip in mTLS-protected Axum endpoints using middleBrick?
Provide a URL that requires client certificate authentication to middleBrick’s scan endpoint. The scan will test the unauthenticated attack surface; to exercise mTLS-protected paths, supply a valid client certificate if the scanner supports custom TLS credentials, or test extraction logic directly in code review and unit tests.