Zip Slip in Actix with Hmac Signatures
Zip Slip in Actix with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Zip Slip is a path traversal vulnerability that occurs when an archive’s entries are extracted without validating that resolved paths remain within the intended directory. In Actix-based services that verify file integrity with Hmac Signatures, the vulnerability can be exposed at two points: during archive processing and during signature verification.
Consider an endpoint that accepts a zip file and an Hmac signature header. The server computes an Hmac over the raw archive bytes using a shared secret, then compares it to the client-supplied signature. If the comparison is performed before path validation, an attacker can supply a malicious archive with crafted paths (e.g., ../../etc/passwd) and a valid Hmac computed over the unmodified archive. The server verifies the Hmac successfully, then extracts entries unsafely, leading to arbitrary file overwrite on the host. This chain—trusting the Hmac while skipping or deferring path canonicalization—means the integrity check passes but the extraction behavior remains unsafe.
Another scenario involves nested archives or specially crafted filenames that, once extracted, resolve outside the target directory. If the Actix service verifies the Hmac on each entry’s payload independently (for example, per-file signatures within the archive), an attacker can embed a valid signature for a benign inner file while the outer path traversal is unchecked. Because the Hmac confirms content authenticity but not safe destination resolution, the service may write files like ../../../malicious to sensitive locations. The vulnerability is not in the Hmac algorithm itself, but in the sequencing and scope of checks: the signature is trusted before the runtime path is confined to an allowed prefix.
In practice, this means the presence of Hmac Signatures can create a false sense of security. An API scanner such as middleBrick, which runs checks including Input Validation and Unsafe Consumption alongside integrity verification patterns, can surface these risky sequences by correlating signature verification steps with extraction or filesystem operations. Developers should ensure that path normalization and directory confinement occur before any trust decisions tied to Hmac Signatures, and that archives are processed in a sandbox that prevents directory traversal regardless of signature validity.
Hmac Signatures-Specific Remediation in Actix — concrete code fixes
To remediate Zip Slip in Actix while using Hmac Signatures, enforce path canonicalization and containment checks before accepting or acting on any extracted entry. Below are concrete, syntactically correct examples that show how to structure the logic safely.
1. Safe extraction with path validation
Normalize each entry path, ensure it is within the intended base directory, and reject paths that escape via .. or absolute positions. Use Rust’s std::path::Path utilities to avoid string-based concatenation pitfalls.
use std::fs; use std::io; use std::path::{Path, PathBuf}; use zip::ZipArchive;
fn safe_extract_zip(zip_path: &Path, base_dir: &Path) -> io::Result<()> {
let file = fs::File::open(zip_path)?;
let mut archive = ZipArchive::new(file)?;
for i in 0..archive.len() {
let mut file = archive.by_index(i)?;
let out_path = normalize_and_confine(&file.name().to_string(), base_dir)?;
if out_path.exists() {
fs::remove_file(&out_path)?;
}
fs::copy(&mut file, &out_path)?;
}
Ok(())
}
fn normalize_and_confine(name: &str, base_dir: &Path) -> io::Result {
let requested = Path::new(name).clean();
if requested.components().any(|c| matches!(c, std::path::Component::ParentDir)) {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "path traversal detected"));
}
let full = base_dir.join(requested);
let full = full.canonicalize().unwrap_or(full);
if full.starts_with(base_dir) {
Ok(full)
} else {
Err(io::Error::new(io::ErrorKind::PermissionDenied, "extraction outside base directory"))
}
}
2. Hmac verification before extraction
Compute and compare the Hmac over the archive bytes prior to any extraction. Use a constant-time comparison to avoid timing attacks, and only proceed if verification succeeds.
use hmac::{Hmac, Mac}; use sha2::Sha256; use zeroize::Zeroize;
type HmacSha256 = Hmac;
fn verify_and_extract(archive_bytes: &[u8], received_sig: &[u8], base_dir: &Path) -> Result<(), String> {
let mut mac = HmacSha256::new_from_slice(secret_key).map_err(|e| e.to_string())?;
mac.update(archive_bytes);
let computed = mac.finalize().into_bytes();
if subtle::ConstantTimeEq::ct_eq(&computed[..], received_sig).into() {
mac.zeroize();
let cursor = std::io::Cursor::new(archive_bytes);
let mut archive = ZipArchive::new(cursor).map_err(|e| e.to_string())?;
for i in 0..archive.len() {
let mut file = archive.by_index(i).map_err(|e| e.to_string())?;
let out_path = normalize_and_confine(&file.name().to_string(), base_dir).map_err(|e| e.to_string())?;
let mut out_file = fs::File::create(out_path).map_err(|e| e.to_string())?;
std::io::copy(&mut file, &mut out_file).map_err(|e| e.to_string())?;
}
Ok(())
} else {
Err("Hmac verification failed".to_string())
}
}
3. Actix handler integration
In an Actix web handler, validate the signature and confinement before processing the uploaded archive. Return clear errors for invalid paths or bad signatures, and avoid extracting to temporary directories that are not explicitly scoped.
use actix_web::{post, web, HttpResponse}; use serde::Deserialize;
#[derive(Deserialize)]
struct UploadPayload {
signature: String,
}
#[post("/upload")]
async fn upload(
payload: web::Form,
body: web::Bytes,
) -> HttpResponse {
let sig = hex::decode(&payload.signature).map_err(|_| HttpResponse::BadRequest().finish())?;
let base = Path::new("/safe/upload/area");
match verify_and_extract(&body, &sig, base) {
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => HttpResponse::BadRequest().body(e),
}
}
By combining strict path canonicalization with Hmac verification performed before extraction, you mitigate Zip Slip while preserving integrity checks. middleBrick’s checks for Input Validation and Unsafe Consumption can help identify risky patterns in your codebase, complementing these manual safeguards.