Rate Limiting Bypass in Feathersjs with Hmac Signatures
Rate Limiting Bypass in Feathersjs with Hmac Signatures — how this specific combination creates or exposes the vulnerability
FeathersJS is a popular framework for building REST and real-time APIs with JavaScript and Node.js. When HMAC signatures are used for request authentication—typically to verify integrity and origin of payloads—the interaction with rate limiting can create bypass conditions. A common pattern is to compute an HMAC over selected request components (method, path, timestamp, body) and send it in a custom header such as x-api-signature. The server independently recomputes the HMAC using a shared secret and compares it to the client-provided value before processing the request. If rate limiting is enforced only after signature verification—or if the rate limiter key does not incorporate the HMAC validity state—an attacker can generate many valid HMAC-signed requests using leaked secrets or predictable nonces without being throttled.
Consider a FeathersJS service that uses HMAC authentication but applies global rate limits based solely on IP address before validating signatures. An attacker who discovers or guesses the shared secret can craft high-volume, correctly signed requests that appear legitimate. Because each request has a unique, valid HMAC and originates from different source IPs (or uses a botnet), IP-based rate limiting fails to curb the abuse. Even when per-client limits are used, if the client identifier is derived from the payload before signature validation, an attacker can vary nonces or timestamps to create distinct identifiers, effectively circumventing counters that rely on stable identifiers.
Another bypass scenario involves replay windows. If the server accepts requests with timestamps within a generous window and rate limiting does not track per-key request counts within that window, an attacker can reuse captured signed requests with different nonces but identical side-effect operations (e.g., creating resources). The signatures remain valid, and the rate limiter may not detect duplicate or near-duplicate operations because it does not correlate requests by signature metadata. This becomes critical for idempotent or state-changing endpoints such as POST /invoices or POST /transfers where a single logical operation can be amplified through multiple signed requests.
Additionally, partial coverage of endpoints can weaken protections. If only public-facing services enforce HMAC and rate limiting together, while internal or legacy services do not, attackers will pivot to the less protected paths. For example, an authenticated HMAC route for /v1/data might be properly rate-limited, but an older /v1/legacy endpoint without signature checks could be called repeatedly. The presence of mixed modes means the overall API security posture is only as strong as the weakest link, and scanning tools like middleBrick can surface such inconsistencies by correlating authentication mechanisms with rate limiting configurations across the API surface.
Real-world findings from scans often map these patterns to OWASP API Top 10 categories such as Broken Object Level Authorization and Improper Rate Limiting. A misalignment between authentication integrity checks (HMAC verification) and enforcement of usage policies allows abuse vectors that resemble credential stuffing or brute force against business logic rather than authentication per se. middleBrick’s checks highlight these gaps by cross-referencing spec definitions with runtime behavior, ensuring that rate limiting is evaluated in the context of each authentication scheme, including Hmac-based flows.
Hmac Signatures-Specific Remediation in Feathersjs — concrete code fixes
To mitigate bypasses, align rate limiting with HMAC validation and ensure each request is counted only after successful integrity and origin checks. Below are concrete patterns for FeathersJS that couple signature verification with per-client rate limiting using a consistent key that includes the client identity derived from the signature or authenticated context.
First, a basic HMAC verification middleware in FeathersJS using Node.js crypto:
const crypto = require('crypto');
function verifyHmac(secret) {
return (context) => {
const { headers, method, url, body } = context.params;
const received = headers['x-api-signature'];
const timestamp = headers['x-request-timestamp'];
const nonce = headers['x-request-nonce'];
if (!received || !timestamp || !nonce) {
throw new Error('Missing authentication headers');
}
// Prevent replay within a short window (e.g., 5 minutes)
const now = Date.now();
const requestTime = parseInt(timestamp, 10);
if (Math.abs(now - requestTime) > 300000) {
throw new Error('Request expired');
}
const payload = `${method}:${url}:${timestamp}:${nonce}:${JSON.stringify(body)}`;
const expected = crypto.createHmac('sha256', secret)
.update(payload)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected))) {
throw new Error('Invalid signature');
}
return context;
};
}
Second, apply rate limiting after verification using a key that binds to the authenticated principal. If your HMAC flow identifies a client via a key ID included in headers (e.g., x-api-key), use that as part of the rate limiter key. Here is an example with a token-bucket style in-memory map; in production, use Redis or similar shared store:
const rateLimitMap = new Map();
const LIMIT = 100; // requests
const WINDOW_MS = 60_000; // 1 minute
function rateLimitByKey() {
return (context) => {
const keyId = context.params.headers['x-api-key'];
const clientKey = `hmac:${keyId}`;
const now = Date.now();
const entry = rateLimitMap.get(clientKey);
if (!entry) {
rateLimitMap.set(clientKey, { count: 1, start: now });
} else {
if (now - entry.start > WINDOW_MS) {
entry.count = 1;
entry.start = now;
} else {
entry.count += 1;
if (entry.count > LIMIT) {
throw new Error('Rate limit exceeded');
}
}
}
return context;
};
}
Ensure ordering: verify HMAC first, then apply rate limiting using identifiers derived from the verified request. Do not count requests before signature validation. In FeathersJS, you can chain hooks so that the HMAC hook runs before the rate limit hook:
app.hooks({
before: {
all: [
verifyHmac(process.env.SHARED_SECRET),
rateLimitByKey()
]
}
});
For multi-service deployments, centralize rate limiting logic by correlating HMAC key IDs with backend services. If you rotate secrets, maintain a short overlap window and include key version in the HMAC payload (e.g., v1:payload...) to avoid abrupt denial of service. middleBrick’s scans can validate that each authenticated path enforces rate limiting consistently and that no endpoint allows high-volume unthrottled requests, reducing the chance of bypass via misconfigured hooks or ordering.
Related CWEs: resourceConsumption
| CWE ID | Name | Severity |
|---|---|---|
| CWE-400 | Uncontrolled Resource Consumption | HIGH |
| CWE-770 | Allocation of Resources Without Limits | MEDIUM |
| CWE-799 | Improper Control of Interaction Frequency | MEDIUM |
| CWE-835 | Infinite Loop | HIGH |
| CWE-1050 | Excessive Platform Resource Consumption | MEDIUM |