Zip Slip in Actix with Bearer Tokens
Zip Slip in Actix with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Zip Slip is a path traversal vulnerability that occurs when an API constructs file paths by directly concatenating user-supplied input with a base directory. In the Actix web framework, this commonly manifests in download or file extraction endpoints where a filename from an archive is appended to a destination path without proper validation. When Bearer Tokens are used for authentication, the presence of a token does not inherently protect file operations; if the endpoint trusts the token for authorization but performs unsafe path joining, an authenticated request can traverse directories outside the intended location.
Consider an Actix endpoint that extracts a user-uploaded ZIP archive. If the server uses the Authorization header with a Bearer Token to identify the user but then uses user-controlled member names to build filesystem paths, an attacker can supply filenames like ../../../etc/passwd. Because ZIP parsers may normalize paths differently than the server, a filename such as ../../../../etc/passwd can escape the extraction root. Even when the request includes a valid Bearer Token, the server might correctly identify the user but incorrectly resolve the file path, leading to unauthorized file reads or overwrites outside the intended directory.
In a typical Actix handler, this vulnerability arises when path concatenation is performed naively, for example using format!("{}/{}", base_dir, filename) without resolving .. segments. An attacker who can control the ZIP contents can craft entries that traverse parent directories. If the endpoint also relies on the Bearer Token for authorization checks only (e.g., ensuring the request is authenticated) but does not enforce path constraints, the combination of authenticated access and unsafe path manipulation increases the impact. The token ensures the request is associated with a user, but it does not prevent path traversal; the server may inadvertently expose configuration files, application sources, or other sensitive resources relative to the extraction root.
middleBrick detects such patterns during black-box scanning by submitting requests with crafted archive members and inspecting responses for unintended file access, regardless of the presence of Bearer Tokens. The scanner exercises path traversal payloads while authenticated with a synthetic token to verify whether authorization boundaries are respected and whether path normalization prevents directory escapes. Findings include evidence of unvalidated path resolution and potential information disclosure, tied to the broader category of Injection and Broken Access Control in the OWASP API Top 10.
To contextualize the risk, consider how the scanner validates exposure: it sends a ZIP containing a file with a traversal name while supplying a valid Bearer Token. If the server returns content from an unexpected location or reveals filesystem details, this indicates a failure in path sanitization. The scanner maps this to relevant compliance frameworks, noting that insecure file handling can violate requirements under SOC2 and GDPR when sensitive data becomes accessible through malformed requests.
Bearer Tokens-Specific Remediation in Actix — concrete code fixes
Remediation focuses on ensuring that user input never directly influences filesystem paths and that path resolution is canonical. In Actix, implement strict validation and use safe path operations rather than string concatenation. Below are concrete code examples that demonstrate secure handling alongside an unsafe pattern to avoid.
Unsafe example to avoid: This handler naively concatenates a user-supplied filename with a base directory and uses a Bearer Token only for authentication, without validating the resulting path:
use actix_web::{web, HttpResponse, HttpRequest};
use std::path::Path;
async fn download_file(
req: HttpRequest,
path: web::Path,
) -> HttpResponse {
let token = req.headers().get("Authorization")
.and_then(|v| v.to_str().ok())
.map(|s| s.strip_prefix("Bearer ").unwrap_or(s));
// Token is checked for existence but path is not sanitized
let filename = path.into_inner();
let base_dir = "/var/app/uploads";
let full_path = format!("{}/{}", base_dir, filename); // Unsafe
if Path::new(&full_path).exists() {
HttpResponse::Ok().body(std::fs::read(&full_path).unwrap_or_default())
} else {
HttpResponse::NotFound().finish()
}
}
This approach is vulnerable because filename can contain traversal sequences, and the token does not mitigate path manipulation.
Secure remediation: Use a path-cleaning library or manually canonicalize the final path to ensure it remains within the intended directory. Validate each path segment and reject filenames containing .. or leading slashes. Combine authentication checks with strict path construction:
use actix_web::{web, HttpResponse, HttpRequest, error::ErrorBadRequest};
use std::path::{Path, PathBuf};
fn safe_join(base: &Path, requested: &str) -> Result {
// Normalize and ensure the resolved path is within base
let requested_path = Path::new(requested);
if requested_path.components().any(|c| matches!(c, std::path::Component::ParentDir)) {
return Err(ErrorBadRequest("Invalid filename"));
}
let mut joined = PathBuf::from(base);
joined.push(requested_path);
// Canonicalize to resolve any symlinks and remove . or ..
match joined.canonicalize() {
Ok(canonical) => {
if canonical.starts_with(base) {
Ok(canonical)
} else {
Err(ErrorBadRequest("Path traversal detected"))
}
}
Err(_) => Err(ErrorBadRequest("Path resolution failed")),
}
}
async fn download_file_secure(
req: HttpRequest,
path: web::Path,
) -> HttpResponse {
// Verify Bearer Token presence and scope as needed
let token = match req.headers().get("Authorization") {
Some(h) if h.to_str().map(|s| s.starts_with("Bearer ")).unwrap_or(false) => {
// Perform token validation/lookup here
true
}
_ => false,
};
if !token {
return HttpResponse::Unauthorized().finish();
}
let filename = path.into_inner();
let base_dir = Path::new("/var/app/uploads");
match safe_join(base_dir, &filename) {
Ok(full_path) => {
if full_path.exists() {
HttpResponse::Ok().body(std::fs::read(full_path).unwrap_or_default())
} else {
HttpResponse::NotFound().finish()
}
}
Err(e) => e.into(),
}
}
In the secure version, safe_join rejects paths containing parent directory components and ensures the canonicalized result remains inside the base directory. The Bearer Token is validated before any file operations, but authorization alone does not replace path safety. For production use, consider additional measures such as filename allowlists and limiting accessible directories.
When integrating with middleware or guards in Actix, apply the same canonicalization logic consistently across all file-serving endpoints. The scanner can verify remediation by attempting traversal while authenticated; a properly secured endpoint will reject traversal attempts and return appropriate error responses without exposing filesystem details.