Path Traversal in Actix with Hmac Signatures
Path Traversal in Actix with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Path Traversal occurs when user-controlled input used to access the file system is not properly validated, allowing sequences like ../ to escape intended directories. In Actix, this risk can be inadvertently introduced when file-serving endpoints rely on path parameters that are concatenated with a base directory without canonicalization. When an endpoint also uses Hmac Signatures to authenticate requests, the interaction between signature verification and path handling becomes a design consideration that can expose traversal possibilities.
Consider an Actix web service that delivers static assets and uses an Hmac Signature to ensure request integrity. The client sends a resource path and an Hmac-SHA256 signature derived from that path plus a shared secret. If the server recomputes the signature over the raw user-supplied path and then uses that same path directly in a filesystem operation (e.g., std::fs::read), an attacker can supply crafted traversal sequences. Even though the signature confirms the client intended that exact path, the server must still resolve the path to a location on disk. Without canonicalization against the allowed base directory, the traversal is successful, potentially exposing files outside the intended directory.
Moreover, if the application uses the signature to gate access to a broader set of resources (e.g., per-user files stored under a common root), an attacker might probe for predictable path patterns. The presence of the Hmac Signature does not reduce the need to treat the path as untrusted input. For example, an endpoint like /files/{file_id} that maps IDs to filesystem paths must ensure the resolved absolute path remains within the designated directory. Signature verification should be viewed as authentication/authorization, not as a substitute for input validation and path sanitization.
In practice, a vulnerable Actix handler might look like this simplified example, where the signature is verified but the path is used without safety checks:
use actix_web::{web, HttpResponse};
use hmac::{Hmac, Mac};
use sha2::Sha256;
type HmacSha256 = Hmac<Sha256>;
async fn get_file(
path: web::Path<String>,
query: web::Query<std::collections::HashMap<String, String>>
) -> HttpResponse {
let signature = match query.get("signature") {
Some(s) => s,
None => return HttpResponse::BadRequest().body("missing signature"),
};
let secret = b"super-secret-key";
let mut mac = HmacSha256::new_from_slice(secret).expect("HMAC can take key of any size");
mac.update(path.as_bytes());
let computed = mac.finalize();
let result = computed.into_bytes();
// naive signature comparison
if format!(Hmac Signatures-Specific Remediation in Actix — concrete code fixes
To mitigate Path Traversal when using Hmac Signatures in Actix, the primary rule is to treat the path parameter as untrusted data and to resolve it against an absolute base directory before any filesystem operation. Signature verification should confirm that the client is allowed to request the intended resource, but it must not replace path canonicalization and strict scope enforcement.
First, implement a helper that safely resolves a requested relative path to a location under a fixed base directory. Use Rust’s standard path methods to normalize and check containment, ensuring the final canonical path starts with the base directory. This prevents ../ escapes regardless of what the Hmac signature validates.
Second, keep the Hmac Signature verification but apply it to a canonicalized or normalized path representation, or at least ensure the original path and the signature input are aligned with the same canonical form. This avoids mismatches where an attacker supplies traversal sequences that the client did not explicitly sign, reducing ambiguity in authorization logic.
Below is a concrete, secure Actix handler demonstrating these practices:
use actix_web::{web, HttpResponse}; use hmac::{Hmac, Mac}; use sha2::Sha256; use std::path::{Path, PathBuf}; type HmacSha256 = Hmac<Sha256>; const BASE_DIR: &str = "/var/www/public"; fn safe_resolve(base: &str, requested: &str) -> Option<PathBuf> { let base_path = Path::new(base).canonicalize().ok()?; let mut target = base_path; target.push(requested.trim_start_matches('/')); target.canonicalize().ok().filter(|p| p.starts_with(&base_path)).map(|p| p.to_path_buf()) } async fn get_file( path: web::Path<String>, query: web::Query<std::collections::HashMap<String, String>> ) -> HttpResponse { let signature = match query.get("signature") { Some(s) => s, None => return HttpResponse::BadRequest().body("missing signature"), }; let secret = b"super-secret-key"; let mut mac = HmacSha256::new_from_slice(secret).expect("HMAC can take key of any size"); // Optionally use a normalized version of the path for signing/verification let normalized = match safe_resolve(BASE_DIR, &path) { Some(p) => p, None => return HttpResponse::Forbidden().body("invalid path"), }; // If you choose to include the raw path in the signature, ensure it matches what the client intended mac.update(path.as_bytes()); let computed = mac.finalize(); let result = computed.into_bytes(); // constant-time comparison would be ideal here let computed_hex = hex::encode(result); if computed_hex != *signature { return HttpResponse::Forbidden().body("invalid signature"); } // Safe to read because safe_resolve enforced containment match std::fs::read(&normalized) { Ok(bytes) => HttpResponse::Ok().body(bytes), Err(_) => HttpResponse::NotFound().body("file not found"), } }Additional recommendations include using framework utilities or crates that provide path sanitization and preferring
Path::joinover string concatenation. For production deployments, combine this approach with the middleBrick CLI to scan from terminal withmiddlebrick scan <url>and verify that your endpoints remain secure against traversal attempts. Teams using CI/CD can add the middleBrick GitHub Action to add API security checks to their pipeline and fail builds if risk scores exceed thresholds, while the Dashboard can help track these scores over time.
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 |