Symlink Attack in Feathersjs with Hmac Signatures
Symlink Attack in Feathersjs with Hmac Signatures — how this specific combination creates or exposes the vulnerability
A symlink attack in a Feathersjs service that uses Hmac Signatures can occur when file uploads or user-controlled paths interact with the service’s authentication and file-resolution logic. In this scenario, an authenticated request (validated via an Hmac Signature) includes a file or path parameter that the server uses to locate or move files. If the server resolves paths by concatenating user input with a base directory and does not canonicalize or validate the final path, an attacker can supply a filename that traverses directories and resolves via a symbolic link outside the intended directory tree.
Because the request is authenticated with an Hmac Signature, the server may trust the request integrity and skip additional authorization checks, assuming that a valid signature implies allowed intent. This trust boundary misalignment is critical: Hmac Signatures protect against tampering in transit but do not enforce path or file-system permissions. An attacker can craft a signed request that writes a file to a temporary location or overwrites a file that is later read by the application with elevated context (for example, a configuration or deployment script). If the application later reads the file through a path derived from attacker-controlled input, it may inadvertently follow a symlink and expose or modify unauthorized resources.
In a Feathersjs application, this often manifests when using hooks or custom file services that accept a filename, build a path, and then move or store files. An attacker who can control filename or directory parameters—while providing a valid Hmac Signature—can leverage directory traversal sequences (e.g., ../../../etc/passwd) or specially crafted names to create or exploit symlinks on the server or in shared storage accessible to the process. The presence of Hmac Signatures does not mitigate path traversal or symlink resolution issues; it only ensures the payload was not altered in transit. Therefore, the combination of trusted Hmac authentication and insufficient path validation increases the risk of unauthorized file access or overwrites via symlink manipulation.
Hmac Signatures-Specific Remediation in Feathersjs — concrete code fixes
Remediation centers on strict path validation, canonicalization, and scoping file operations to a designated directory, independent of the Hmac verification step. Hmac verification should remain focused on payload integrity, while file handling must enforce allowlists, reject traversal sequences, and resolve paths to ensure they remain within the intended directory.
Example: Secure file service with Hmac Signatures in Feathersjs
const crypto = require('crypto');
const path = require('path');
const fs = require('fs').promises;
// Hmac signature verification middleware
function verifyHmac(req, res, next) {
const signature = req.headers['x-hmac-signature'];
const body = req.body; // or raw payload depending on setup
const secret = process.env.HMAC_SECRET;
const computed = crypto.createHmac('sha256', secret).update(JSON.stringify(body)).digest('hex');
if (!signature || computed !== signature) {
return next(new Error('Invalid signature'));
}
next();
}
// Canonicalize and validate file path
function resolveSafeFile(baseDir, userFilename) {
// Reject paths that attempt traversal or contain absolute indicators
if (userFilename.includes('..') || path.isAbsolute(userFilename)) {
throw new Error('Invalid filename');
}
// Normalize and ensure the resolved path is inside baseDir
const resolved = path.resolve(baseDir, userFilename);
if (!resolved.startsWith(path.resolve(baseDir))) {
throw new Error('Path traversal detected');
}
return resolved;
}
// Feathers service hook
async function secureFileHook(hook) {
const baseDir = path.resolve('/var/app/uploads');
const file = hook.data.file; // assume multipart/form-data parsed elsewhere
const filename = file.name;
const safeName = Date.now() + '-' + filename.replace(/[^a-z0-9._-]/gi, '_');
const destination = resolveSafeFile(baseDir, safeName);
await fs.writeFile(destination, file.buffer);
hook.result = { path: destination };
return hook;
}
module.exports = function (app) {
app.use('/files', verifyHmac, {
async create(data, params) {
// Simulated file handling; in practice use a multipart parser
const hook = { data, result: null };
return secureFileHook(hook).then(() => hook.result);
}
});
};
Key practices illustrated:
- Validate and canonicalize paths before any filesystem operation; reject
..and absolute paths explicitly. - Do not rely on Hmac Signature validity to imply safe file paths; treat signature checks as integrity-only.
- Use a secure base directory and store files with generated names to avoid symlink injection through original filenames.
- If your application interacts with external storage or shared mounts, ensure the runtime user has minimal permissions to reduce the impact of any successful symlink manipulation.
Frequently Asked Questions
Does a valid Hmac Signature guarantee a request is safe from path traversal or symlink abuse?
What should I do if my Feathersjs service accepts file uploads alongside Hmac authentication?
path.resolve and enforce they remain within a scoped directory. Use generated filenames and avoid using user-controlled paths directly in filesystem operations.