Symlink Attack in Feathersjs with Api Keys
Symlink Attack in Feathersjs with Api Keys — how this combination creates or exposes the vulnerability
A symlink attack in a FeathersJS service that uses API keys occurs when an attacker can leverage predictable or poorly scoped file operations to read or overwrite files outside the intended directory. In FeathersJS, services often handle file uploads, store metadata in a database, and may serve files via static paths. If the service resolves user-supplied filenames or paths without strict validation and uses API key authentication for access control, an attacker can supply a crafted path containing ../ sequences or a symbolic link to traverse directories. When the API key is accepted but the path is not canonicalized, the server may follow the symlink and expose files that should be protected, such as configuration or credential files.
FeathersJS typically uses hooks to process parameters before service methods run. If a hook adds a file path to a model and later a filesystem operation uses that path without resolving it to a real, canonical location, the effective authorization (API key) is decoupled from the actual file access. For example, an API key intended to allow read-only access to user avatars may be accepted, but if the service resolves filename: '../../etc/passwd' and follows the symlink, the operation may succeed with elevated access to system files. This combination of weak path handling and key-based authorization creates a bypass where the API key appears valid but the underlying file access is unsafe.
Consider a FeathersJS service that stores uploaded files under /app/uploads and uses a hook to set file.path. If the hook does not sanitize the filename and the service later constructs a URL or serves the file using a path built from user input, an attacker can provide a filename like ../../../var/lib/app/config.yaml. Even with a valid API key, the service may read sensitive configuration because the path escapes the intended directory. In an OpenAPI spec analyzed by middleBrick, such patterns appear as missing path canonicalization and overly permissive file operations, which correlate with findings in the Property Authorization and Input Validation checks. The scanner highlights these as high-severity issues because an authenticated client (with a valid API key) can reach unintended resources through path traversal.
LLM-specific risks are not the focus here, but it is worth noting that output scanning and prompt injection tests do not mitigate filesystem traversal. The core issue is input validation and path resolution in service logic. middleBrick’s checks for BFLA/Privilege Escalation and Property Authorization are designed to detect cases where authorization does not properly constrain file operations, which often maps to this class of vulnerability in FeathersJS apps.
Api Keys-Specific Remediation in Feathersjs — concrete code fixes
Remediation centers on strict path validation, canonical resolution, and ensuring API key checks are coupled with filesystem boundaries. Do not rely on API keys alone to protect file paths; enforce directory constraints in service hooks and filesystem utilities.
Example secure FeathersJS service with API keys and path hardening:
const path = require('path');
const fs = require('fs').promises;
module.exports = function (app) {
const apiKeys = new Set(['trusted-key-1', 'trusted-key-2']);
app.use('/files', {
async before(context) {
const { apikey, filename } = context.data;
if (!apikey || !apiKeys.has(apikey)) {
throw new Error('Unauthorized');
}
if (!filename || typeof filename !== 'string') {
throw new Error('Invalid filename');
}
// Canonicalize and restrict to upload directory
const base = path.resolve(app.get('uploadsDir'));
const target = path.resolve(base, filename);
if (!target.startsWith(base)) {
throw new Error('Path traversal attempt');
}
// Ensure symlinks resolve inside base
try {
const resolved = await fs.realpath(target);
if (!resolved.startsWith(base)) {
throw new Error('Symlink escape attempt');
}
context.path = resolved;
} catch (err) {
throw new Error('Invalid file path');
}
},
async create(context) {
const { context: filePath } = context.path;
// Example: store file securely
await fs.writeFile(filePath, context.data.content);
return { path: filePath };
}
});
};
Key points in the example:
- API key validation is performed in
beforehook and rejects missing or unknown keys. - Filename is checked for type and then combined with a resolved base directory using
path.resolve. - A strict prefix check ensures the resolved target remains inside the allowed directory.
fs.realpathresolves symlinks; a second prefix check prevents symlink-based escapes.- The canonical path is stored and used for filesystem operations, decoupling API key authorization from raw user input.
In the dashboard, teams can track scans over time to ensure these fixes reduce findings in Property Authorization and Input Validation. The CLI can be integrated into testing with middlebrick scan <url> to verify that the endpoint no longer exposes traversal risks. For teams using CI/CD, the GitHub Action can fail builds if risk scores degrade, while the MCP Server lets developers scan APIs directly from their coding assistant during edits.