Zip Slip in Actix with Dynamodb
Zip Slip in Actix with Dynamodb — how this specific combination creates or exposes the vulnerability
Zip Slip is a path traversal vulnerability that occurs when user-supplied archive entries are not properly validated before being extracted, allowing files to be written outside the intended directory. In an Actix web service that interacts with Amazon DynamoDB, this risk arises when an application accepts file uploads or archive downloads, extracts them on the filesystem, and then stores or retrieves related metadata in DynamoDB without validating path components.
Consider an endpoint that receives a ZIP file containing filenames that may include sequences like ../. If the server extracts these entries directly into a base directory such as /var/app/uploads/, a malicious archive can traverse outside that directory and overwrite system or application files. Even when DynamoDB is used to track file metadata (for example, storing object keys, user IDs, or extraction logs), the vulnerability exists at the extraction layer, not in the database itself. However, an attacker who achieves path traversal may manipulate which records are written to or read from DynamoDB by controlling filename-based keys or by corrupting stored objects, thereby leveraging insecure trust between the filesystem and the database.
In an Actix application, typical contributing factors include:
- Using archive extraction libraries that do not sanitize path entries against directory traversal patterns.
- Constructing DynamoDB keys or query conditions from unchecked filename or user input derived from the archive.
- Assuming that server-side validation or separate access controls are sufficient without validating path segments individually.
Dynamodb-Specific Remediation in Actix — concrete code fixes
To remediate Zip Slip in an Actix application that uses DynamoDB, ensure that every filename extracted from an archive is validated and normalized before any filesystem operation or database interaction. Do not trust entries from the archive, and avoid building paths via string concatenation with user input.
1. Validate and sanitize each entry using a safe extraction library or manual checks. For example, using the zip-rs crate, you can inspect each entry and reject paths containing .. or that do not reside within the target directory after resolution:
use std::path::{Path, PathBuf};
fn safe_path<'a>(base: &Path, file_name: &'a str) -> Option<&'a Path> {
let full = base.join(file_name).canonicalize().ok()?;
if full.starts_with(base) {
Some(full)
} else {
None
}
}
// In an Actix handler, use it like:
// let entry_path = safe_path(upload_base, entry.name())?;
2. When storing or retrieving items in DynamoDB, derive keys from sanitized paths or use a mapping that does not expose filesystem structure. For example, use a hashed key or a UUID mapped to metadata, and store the original filename as an attribute without using it to construct key segments:
use aws_sdk_dynamodb::types::AttributeValue;
let item = vec![
("file_id", AttributeValue::S(uuid::Uuid::new_v4().to_string())),
("original_name", AttributeValue::S(sanitized_filename.to_string())),
("user_id", AttributeValue::S(user_id.clone())),
];
// Then put_item with this constructed item map.
3. When querying DynamoDB, avoid passing raw user-controlled strings directly into key expressions. Use parameterized queries or strongly-typed structures to prevent injection or path-based logic errors:
let user_id = "example_user";
let response = client
.query()
.table_name("FileMetadata")
.key_condition_expression("user_id = :uid")
.expression_attribute_values({
let mut map = std::collections::HashMap::new();
map.insert(":uid".to_string(), AttributeValue::S(user_id.to_string()));
map
})
.send()
.await?;
4. In Actix, structure your routes and extractors to validate inputs before they reach business logic. Combine multipart form parsing with explicit checks, and ensure that any interaction with DynamoDB uses values that have passed validation, not raw filenames from the archive.
async fn upload_file(
payload: Multipart,
upload_base: web::Data<Path>,
client: web::Data<aws_sdk_dynamodb::Client>,
) -> Result<HttpResponse, Error> {
let mut dir = upload_base.to_path_buf();
while let Some(item) = payload.try_next().await? {
let mut f = item.open().await?;
let filename = item.name().to_string();
if let Some(target) = safe_path(&dir, &filename) {
let mut dst = File::create(&target).await?;
while let Some(chunk) = f.try_next().await? {
dst.write_all(&chunk).await?;
}
// Store sanitized metadata in DynamoDB
let item = vec![
("path_hash", AttributeValue::S(sha256::digest(target.to_string_lossy().as_ref()))),
("original_name", AttributeValue::S(filename)),
];
// put_item omitted for brevity
} else {
return Err(error::ErrorBadRequest("Invalid path"));
}
}
Ok(HttpResponse::Ok().finish())
}
By validating paths on extraction, avoiding filename-based key construction in DynamoDB, and using parameterized queries, you eliminate the conditions that enable Zip Slip in an Actix service while maintaining a safe interaction model with DynamoDB.