Zip Slip in Axum with Api Keys
Zip Slip in Axum with Api Keys — how this specific combination creates or exposes the vulnerability
Zip Slip is a path traversal vulnerability that occurs when an API constructs file paths from user-supplied input without proper validation. In an Axum application that uses API keys for authentication, the risk emerges when key-based routing or authorization logic inadvertently incorporates unchecked user input into filesystem operations. For example, an endpoint that accepts an API key and a filename parameter may use the key to identify a tenant or scope, then join that key value directly with a user-provided filename to read or write files. If the filename contains sequences like ../, the resulting path can escape the intended directory, exposing or overwriting files outside the allowed scope. This is especially dangerous when API keys are used to derive directory names or to enforce tenant isolation, because a malicious actor who discovers or guesses a valid key might leverage path traversal to access files belonging to other tenants or the host system.
In Axum, handlers often receive the API key via headers (e.g., Authorization or a custom x-api-key) and use them alongside path or query parameters. Consider a handler that retrieves configuration files scoped by API key: the key determines a subdirectory under a base directory. If the handler concatenates the key with a filename provided by the client without canonicalizing or validating the resulting path, an attacker can supply a crafted filename such as ../../../etc/passwd. The traversal sequences move up the directory tree, bypassing the intended scoping based on the API key. Because the scan testing performed by middleBrick includes input validation checks, such unsafe path construction would be flagged as a high-severity finding. The presence of API keys does not inherently mitigate or cause Zip Slip; it is the combination of key-based scoping and insufficient path sanitization that expands the attack surface.
Additionally, if the API key itself is exposed in logs, error messages, or through insecure storage, an attacker who obtains the key might probe for path traversal endpoints. middleBrick’s LLM/AI Security checks do not apply here, but its input validation and data exposure scans help identify risky patterns in request handling and data leakage. The vulnerability is not about the API key format or encryption; it is about how the key and user input are combined before interacting with the filesystem. Proper validation, canonical path resolution, and scope-bound access controls are required to ensure that even when API keys are used for routing or tenant identification, file operations remain confined to authorized directories.
Api Keys-Specific Remediation in Axum — concrete code fixes
To remediate Zip Slip in an Axum service that uses API keys, ensure that API key values are never directly concatenated with user-controlled paths. Instead, treat the API key as an opaque identifier for authorization and scope resolution, and keep all filesystem operations within strictly controlled base directories. Use Rust’s path sanitization utilities and validate that resolved paths remain within the intended directory tree.
Below is a safe pattern for handling API-key-scoped file access in Axum. The example uses std::path::Path operations to prevent directory traversal and demonstrates how to integrate API key scoping without exposing the system to Zip Slip.
use axum::{routing::get, Router};
use std::path::{Path, PathBuf};
use std::sync::Arc;
struct AppState {
base_dir: PathBuf,
// In practice, validate the key against a store or middleware before reaching the handler
}
async fn get_config(
State(state): State>,
axum::extract::Query(params): axum::extract::Query::>,
) -> Result {
let api_key = params.get("api_key").ok_or((axum::http::StatusCode::UNAUTHORIZED, "Missing api_key".into()))?;
let file_name = params.get("file").ok_or((axum::http::StatusCode::BAD_REQUEST, "Missing file parameter".into()))?;
// Validate file_name: only allow safe filenames
if !file_name.chars().all(|c| c.is_alphanumeric() || c == '.' || c == '-' || c == '_') {
return Err((axum::http::StatusCode::BAD_REQUEST, "Invalid filename".into()));
}
// Build a tenant-specific subdirectory using the API key, but sanitize the key for filesystem use
let tenant_dir = sanitize_path_component(api_key);
let base = &state.base_dir;
let target = base.join(tenant_dir).join(file_name);
// Ensure the resolved path is within the base directory
if !target.starts_with(base) {
return Err((axum::http::StatusCode::FORBIDDEN, "Path traversal attempt detected".into()));
}
// Read file safely
match tokio::fs::read_to_string(&target).await {
Ok(content) => Ok(content),
Err(_) => Err((axum::http::StatusCode::NOT_FOUND, "File not found".into())),
}
}
fn sanitize_path_component(s: &str) -> String {
s.chars().filter(|c| c.is_alphanumeric() || c == '-' || c == '_' || c == '.').collect()
}
#[tokio::main]
async fn main() {
let base_dir = PathBuf::from("/safe/base/dir");
let state = Arc::new(AppState { base_dir });
let app = Router::new()
.route("/config", get(get_config))
.with_state(state);
// axum server setup omitted
}
Key takeaways: the API key is used only to select a subdirectory after sanitization, and the final path is verified to remain inside the base directory using starts_with. This prevents Zip Slip regardless of malicious sequences in the filename parameter. middleBrick’s CLI (middlebrick scan <url>) can be integrated into development workflows to detect similar path handling issues during testing.