Zip Slip in Feathersjs with Hmac Signatures
Zip Slip in Feathersjs with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Zip Slip is a path traversal vulnerability that occurs when a server extracts or constructs file paths using unsanitized user input. In Feathersjs applications that use Hmac Signatures for file operations or API authentication, the combination of dynamic path resolution and signature validation can inadvertently expose or amplify the risk.
Consider a Feathers service that accepts a filename and an Hmac signature to authorize access to stored assets. If the service uses the user-supplied filename directly to build filesystem paths—such as joining it with a base directory without canonicalization—an attacker can provide a crafted path like ../../../etc/passwd. Even when the Hmac Signature validates the request, the resolved path may escape the intended directory because the signature does not guarantee safe path construction.
In Feathersjs, services often integrate with adapters or custom hooks that interact with storage backends. When Hmac Signatures are used to sign URLs or tokens for file access, the client may supply a path parameter that the server uses to locate a file. If the server does not validate or sanitize the path before joining it with the storage root, the signature verification passes while the traversal succeeds. This means an authenticated (via Hmac) request can read or overwrite arbitrary files, turning a missing path validation issue into a data exposure vector.
Real-world examples include services that generate pre-signed URLs using Hmac and later serve files based on user-controlled keys. If the key maps to a user-supplied path without normalization, an attacker can manipulate the key to traverse directories. While the Hmac Signature ensures the request was generated by a party with knowledge of the secret, it does not prevent the server from interpreting malicious path segments. This highlights that Hmac Signatures provide integrity and authenticity for requests but do not inherently protect against insecure path handling.
OWASP API Top 10 categories such as Broken Object Level Authorization (BOLA) and Path Traversal are relevant here. A Zip Slip in this context may allow unauthorized data exposure or even remote code execution if the traversed file is interpreted by the server. middleBrick scans would flag such issues under Data Exposure and Input Validation checks, especially when OpenAPI specs define path parameters that feed into filesystem operations without proper schema constraints.
Hmac Signatures-Specific Remediation in Feathersjs — concrete code fixes
Remediation focuses on strict path validation and avoiding direct use of user input in filesystem paths, even when requests carry valid Hmac Signatures. Below are concrete Feathersjs code examples demonstrating secure handling.
Secure Path Resolution with Hmac Validation
Use a dedicated library to normalize and validate paths, ensuring they remain within the allowed directory. Combine this with Hmac verification before any file operation.
const feathers = require('@feathersjs/feathers');
const express = require('@feathersjs/express');
const crypto = require('crypto');
const path = require('path');
const fs = require('fs-extra');
const app = express(feathers());
// Middleware to verify Hmac Signature for file routes
function verifyHmac(req, res, next) {
const { signature, filename } = req.query;
const secret = process.env.HMAC_SECRET;
const computed = crypto.createHmac('sha256', secret)
.update(filename)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(computed))) {
return res.status(403).json({ message: 'Invalid signature' });
}
next();
}
// Service hook to safely resolve file paths
app.use('/files', {
async before(hook) {
const { filename } = hook.params.query;
const baseDir = '/safe/upload/dir';
const resolvedPath = path.resolve(baseDir, filename);
// Ensure the resolved path is within baseDir
if (!resolvedPath.startsWith(path.resolve(baseDir))) {
throw new Error('Invalid path: traversal detected');
}
// Attach safe path for downstream use
hook.params.safePath = resolvedPath;
return hook;
},
async get(id, params) {
// Use safePath from hook, not user input directly
const filePath = params.safePath;
if (!(await fs.pathExists(filePath))) {
throw new Error('File not found');
}
return fs.readFile(filePath, 'utf-8');
}
});
// Apply Hmac verification before file operations
app.service('files').hooks({
before: {
all: [verifyHmac]
}
});
Input Validation and Schema Constraints
Define strict input schemas for path parameters to prevent traversal sequences. Use feathers-hooks-common or custom validators to reject paths containing .. or absolute indicators.
const { iff, isProvider, preventChanges } = require('feathers-hooks-common');
const { sanitize } = require('feathers-commons');
function validatePath() {
return async context => {
const { filename } = context.params.query || {};
if (!filename) throw new Error('Filename is required');
// Reject dangerous sequences
if (filename.includes('..') || path.isAbsolute(filename)) {
throw new Error('Invalid filename');
}
// Normalize and store safe version
context.params.safeFilename = path.normalize(filename).replace(/^(\.\.[/\\])+/, '');
return context;
};
}
app.service('files').hooks({
before: {
create: [validatePath()]
}
});
Additional Measures
- Use a allowlist for filenames (e.g., alphanumeric, underscores, dashes) and reject anything outside the pattern.
- Serve files via a dedicated endpoint that maps IDs to stored paths, avoiding direct user path input.
- Combine with middleBrick scans to detect insecure path handling and missing validation in your OpenAPI spec.