Injection Flaws in Feathersjs with Hmac Signatures
Injection Flaws in Feathersjs with Hmac Signatures — how this specific combination creates or exposes the vulnerability
FeathersJS is a framework for real-time applications that often exposes REST and Socket endpoints. When Hmac Signatures are used for verifying request integrity, developers typically compute a signature on the client with a shared secret and send it in a header. If the server uses this signature to build dynamic queries without additional validation, an Injection Flaw can occur. For example, a signature may be computed over a user-controlled query parameter such as sort or filter. If the server trusts the signed value and directly interpolates it into a database query, an attacker who can influence the signed content may inject operators or commands.
Consider a scenario where the client sends sort=createdAt and the server recomputes the Hmac to verify integrity. If the implementation does not strictly whitelist allowed fields, an attacker could sign sort=createdAt; DROP TABLE users (or a NoSQL injection payload for a document store) and have the server accept it as valid. Because middleBrick tests Authentication, BOLA/IDOR, and Unsafe Consumption in parallel, it can detect whether an endpoint that uses Hmac Signatures reflects untrusted data into execution contexts without proper encoding or validation.
Another pattern involves query building based on signed request metadata. If the server uses a signed where clause and directly passes it to an ORM or adapter, an attacker who obtains or guesses a valid signature (for instance, via a leaked secret or a replayed request) can inject conditions such as { $ne: null } to bypass authorization. This intersects with BOLA/IDOR when object-level permissions are not rechecked after the data is retrieved. Data Exposure and Encryption checks performed by middleBrick help identify whether sensitive information is returned as a result of such injected filters.
In a FeathersJS service that also exposes an unauthenticated LLM endpoint for generating documentation or examples, an attacker might attempt prompt injection or data exfiltration through signed inputs that reach the LLM layer. For instance, if a signed user message is forwarded to an LLM without sanitization, malicious instructions could be smuggled through the Hmac-verified payload. The LLM/AI Security probes of middleBrick specifically test for system prompt leakage, jailbreak attempts, and output exposure of API keys or PII, which may be triggered via injected content in signed fields.
Real-world injection patterns in FeathersJS with Hmac Signatures include NoSQL injection in Mongoose adapters, SQL injection when the signature influences raw query strings, and command injection if signed values are passed to shell-like operations. Because the scanner runs 12 checks in parallel and includes Inventory Management and Property Authorization, it can surface cases where signed inputs affect access control or data exposure. Remediation focuses on strict input validation, canonicalization of signed fields, and avoiding direct use of signed content in execution contexts.
Hmac Signatures-Specific Remediation in Feathersjs — concrete code fixes
To remediate Injection Flaws when using Hmac Signatures in FeathersJS, you must decouple trust from user-influenced data, even if it is covered by the signature. Treat the signature as a integrity check, not as authorization or validation. The following examples demonstrate secure patterns.
1. Whitelist allowed fields for sorting and filtering
Instead of directly using a signed sort parameter, validate it against a predefined set of fields. This prevents injection of operators or malicious expressions.
// client.js: Compute Hmac over allowed, canonical parameters
const crypto = require('crypto');
const secret = 'shared-secret';
const params = { sort: 'createdAt', limit: '10' };
const payload = `sort=${params.sort}&limit=${params.limit}`;
const signature = crypto.createHmac('sha256', secret).update(payload).digest('hex');
console.log({ params, signature });
On the server, recompute the signature and enforce a whitelist:
// server.js
const crypto = require('crypto');
const secret = 'shared-secret';
function verifyRequest(query) {
const { sort, limit, signature } = query;
const expectedPayload = `sort=${sort}&limit=${limit}`;
const expectedSignature = crypto.createHmac('sha256', secret).update(expectedPayload).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature))) {
throw new Error('Invalid signature');
}
const allowedSortFields = new Set(['createdAt', 'updatedAt', 'name']);
if (sort && !allowedSortFields.has(sort)) {
throw new Error('Invalid sort field');
}
const allowedLimits = new Set(['10', '25', '50']);
if (limit && !allowedLimits.has(limit)) {
throw new Error('Invalid limit');
}
return { sort, limit };
}
app.get('/api/items', (req, res) => {
try {
const { sort, limit } = verifyRequest(req.query);
// Use parameterized query or ORM methods; never directly interpolate sort/limit
itemsService.find({ $sort: sort, $limit: Number(limit) })
.then(data => res.json(data));
} catch (err) {
res.status(400).send({ error: err.message });
}
});
2. Use parameterized queries instead of dynamic injection
When the signed data affects database queries, ensure that the query structure is static and only data values are parameterized.
// server.js with an ORM-like adapter
const { authenticate } = require('feathers-authentication');
const crypto = require('crypto');
app.use('/api/reports', authenticate());
app.service('reports').hooks({
before: {
create: [context => {
const { filter, signature } = context.data;
const secret = process.env.HMAC_SECRET;
const expected = crypto.createHmac('sha256', secret).update(JSON.stringify({ filter })).digest('hex');
if (expected !== signature) {
throw new Error('Invalid Hmac');
}
// Validate filter keys and values strictly
const allowedKeys = new Set(['status', 'type']);
for (const key of Object.keys(filter)) {
if (!allowedKeys.has(key)) {
throw new Error('Invalid filter key');
}
}
// Build query using parameterized conditions
context.params.query = {
where: {
status: filter.status, // ORM will parameterize this
type: filter.type
}
};
return context;
}]
}
});
3. Separate integrity from authorization and avoid executing signed strings
Never use signed values as code, eval, or raw query strings. If you must include dynamic values, map them to safe references after validation.
// Avoid this pattern:
// const query = signedData; // Do NOT do this
// Prefer this pattern:
const crypto = require('crypto');
function buildSafeQuery(signedPayload) {
const secret = process.env.HMAC_SECRET;
const expected = crypto.createHmac('sha256', secret).update(signedPayload).digest('hex');
if (expected !== receivedSignature) throw new Error('Bad signature');
const parsed = JSON.parse(signedPayload);
// Map to internal IDs, not raw injection
return {
userId: mapToKnownUserId(parsed.userId), // validation function
includeArchived: Boolean(parsed.includeArchived)
};
}
4. Enforce rate limiting and logging
Even with Hmac Signatures, ensure that endpoints using signed inputs have rate limiting to reduce brute-force or replay risks. middleBrick’s Rate Limiting check can surface endpoints that lack appropriate controls.
5. Scan with middleBrick to validate fixes
After applying these patterns, use the middleBrick CLI to verify that the endpoint no longer exposes unsafe consumption or injection risks. Run middlebrick scan <url> to get a security risk score and prioritized findings. If you integrate the GitHub Action, you can fail builds when risk scores drop below your chosen threshold, and the MCP Server allows you to scan APIs directly from your IDE while developing fixes.