Path Traversal in Actix with Api Keys
Path Traversal in Actix with Api Keys — how this specific combination creates or exposes the vulnerability
Path Traversal occurs when an API endpoint uses user-supplied input to construct file system paths without adequate validation or sanitization. In Actix web applications, this typically manifests through route parameters, query strings, or request headers that influence which file is opened. When Api Keys are used for identification but not tightly coupled to authorization checks, the risk profile changes in two ways.
First, an Api Key can grant access to a broader set of endpoints or data directories than intended. If a key is scoped to a tenant or customer, but the application resolves paths like format!("/var/data/{}", user_input) without enforcing tenant boundaries, a request with a valid key can traverse outside the allowed directory. Second, logging and audit mechanisms that include the Api Key can inadvertently expose keys in log files stored in parent directories if path construction is not controlled, creating a secondary exposure path.
Consider an Actix handler that serves user documents:
use actix_web::{web, HttpResponse, Responder};
use std::path::PathBuf;
async fn get_document(
path: web::Path,
api_key: web::Header<String>,
) -> impl Responder {
let base = "/srv/documents";
let requested = path.into_inner();
let full_path = PathBuf::from(base).join(requested);
// Unsafe: no validation of `full_path` is within base
actix_files::NamedFile::open(full_path).map_err(|e| ... )
}
If an attacker provides ../../../etc/passwd as path, the resolved path escapes the intended directory. The presence of a valid Api Key in the header does not mitigate this; it only confirms that the request comes from an authenticated client, not that the requested resource is authorized. This is distinct from authentication failures because the key is accepted, but path resolution is unsafe.
The interaction with middleware can exacerbate the issue. If an Actix middleware extracts the Api Key and attaches it to request extensions for downstream handlers, but those handlers later use unchecked user input in paths, the key’s presence creates a false sense of security. The key identifies the caller but does not constrain the filesystem namespace the caller is allowed to traverse.
Real-world analogies include CVE-2021-23352 patterns where path traversal in Rust web servers allowed reading arbitrary files when query parameters were concatenated without normalization. In an Actix context, the same class of vulnerability emerges whenever PathBuf::join or similar operations are driven by unchecked input, regardless of how the request was authenticated.
Api Keys-Specific Remediation in Actix — concrete code fixes
Remediation focuses on ensuring that Api Key authentication is paired with strict path confinement and input validation. Do not rely on the key alone to enforce data isolation; enforce it in the handler logic.
1. Use a base directory and validate that the resolved path remains within it. Prefer canonicalize and prefix checks:
use actix_web::{web, HttpResponse, Responder};
use std::path::{Path, PathBuf};
async fn get_document(
path: web::Path,
api_key: web::Header<String>,
// Assume a function that maps key to allowed tenant directory
key_to_dir: web::Data<KeyToDirService>,
) -> impl Responder {
let requested = path.into_inner();
let tenant_dir = key_to_dir
.get_dir_for_key(&api_key)
.unwrap_or("/srv/documents"); // fallback
let base = Path::new(tenant_dir);
let requested_path = Path::new(&requested);
// Normalize and resolve
let resolved = base.join(requested_path).canonicalize().map_err(|e| {
actix_web::error::ErrorBadRequest("invalid path")
})?;
// Ensure the resolved path is still within base after canonicalization
if !resolved.starts_with(base) {
return HttpResponse::Forbidden().finish();
}
actix_files::NamedFile::open(resolved).map_err(|e| ... )
}
2. If Api Keys are tied to specific scopes or prefixes, encode those constraints in the path itself. For example, if each key maps to a subdirectory, include that subdirectory as a static segment and reject any user input that attempts to escape it:
use actix_web::web;
use std::path::PathBuf;
async fn scoped_get(
path: web::Path<(String, String)>, // (key_segment, user_file)
) -> Result<impl Responder, actix_web::Error> {
let (key_segment, user_file) = path.into_inner();
// key_segment is part of the route or derived from Api Key mapping
let mut full = PathBuf::from("/srv/data");
full.push(key_segment);
// Further restrict: do not allow additional traversal beyond key_segment
if user_file.contains("..") || user_file.starts_with('/') {
return Err(actix_web::error::ErrorBadRequest("invalid"));
}
full.push(user_file);
// ... open file
Ok(actix_files::NamedFile::open(full)?)
}
3. Leverage Actix extractors to centralize path validation. Create a custom extractor that normalizes and bounds the path before it reaches the handler:
use actix_web::{dev::Payload, FromRequest, HttpMessage, HttpRequest};
use std::future::{ready, Ready};
use std::path::PathBuf;
struct SafePath(PathBuf);
impl FromRequest for SafePath {
type Error = actix_web::Error;
type Future = Ready<Result<Self, Self::Error>>;
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
let path_str = req.match_info().get("path");
match path_str {
Some(raw) => {
// Basic guard: reject sequences that escape
if raw.contains("..") || raw.ends_with('/') {
ready(Err(actix_web::error::ErrorBadRequest("invalid")))
} else {
let base = PathBuf::from("/srv/tenant");
let resolved = base.join(raw).canonicalize().unwrap_or(base);
if resolved.starts_with(&base) {
ready(Ok(SafePath(resolved)))
} else {
ready(Err(actix_web::error::ErrorForbidden()))
}
}
}
None => ready(Err(actix_web::error::ErrorBadRequest("missing path"))),
}
}
}
// Usage in route:
// async fn handler(safe: SafePath) { ... }
These examples show how Api Key context can inform directory scoping while path validation remains strict. The key enables tenant selection, but the handler must still enforce filesystem boundaries explicitly.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |
Frequently Asked Questions
Can middleware that reads Api Keys inadvertently enable path traversal?
How does middleBrick report on Path Traversal in Actix with Api Keys?
../../../etc/passwd). It checks whether the endpoint respects intended boundaries and reports findings with severity and remediation guidance, mapping to relevant frameworks like OWASP API Top 10.