Memory Leak in Feathersjs with Jwt Tokens
Memory Leak in Feathersjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability
A memory leak in a Feathersjs application using Jwt Tokens typically arises when token-related data or caches grow unbounded during request processing. Feathers services often attach decoded payloads to the connection object (e.g., app.channel('authentication').hooks({ before: hook => { hook.params.tokenPayload = verify(jwt, secret); } })) and retain references across requests or channels. If these references are stored in global maps keyed by user ID or token identifier without cleanup, objects accumulate and increase heap usage over time.
In a long-running service, this pattern can keep payloads, decoded claims, and associated metadata alive well beyond the request lifecycle. Because Jwt Tokens are self-contained and often carry additional metadata or large custom claims, the retained size per request can be non-trivial. When many concurrent users authenticate and their payloads are cached naively, the process memory footprint grows steadily, leading to increased garbage collection pressure, higher latency, and eventual degradation or restarts. This is especially observable in setups where channels remain open (e.g., WebSocket transports) and service hooks retain per-channel state tied to token data.
The vulnerability is not in the Jwt Tokens specification itself but in how Feathersjs manages state and references around token payloads. Without explicit cleanup or bounded caches, token payloads and derived objects can become unintentionally long-lived. The unauthenticated scan capability of middleBrick can surface this class of issue by flagging abnormal resource growth patterns and insecure handling of authentication state, emphasizing the need for disciplined memory management when Jwt Tokens are used throughout the service layer.
Jwt Tokens-Specific Remediation in Feathersjs — concrete code fixes
Remediation focuses on avoiding long-lived references to token payloads and ensuring transient, scoped usage. Do not attach decoded Jwt Tokens to global or long-lived objects; instead, pass only required claims as primitives (strings, numbers) and clear references explicitly after use.
// Feathers hook: attach only needed claims, avoid retaining full payload
const { verify } = require('jsonwebtoken');
module.exports = function () {
return async context => {
const token = context.params.headers.authorization?.split(' ')[1];
if (!token) { return context; }
const payload = verify(token, process.env.JWT_SECRET);
// Keep only necessary, primitive values
context.params.userId = payload.sub;
context.params.role = payload.role;
// Explicitly remove reference to the full payload if not needed downstream
delete context.params.token;
return context;
};
};
For channel-based communication, ensure per-channel state does not retain token-derived objects. Use weak structures or explicit cleanup hooks to release references when connections close.
// Feathers channel cleanup to release references
const tokenCache = new Map();
app.channel('connection', () => ({ users: new Map() }));
app.channel((connection) => {
// connection can be an object with user id
return connection.users.has(connection.userId);
});
app.channel(channel => {
// On leave, remove references to token-derived data
channel.users.delete(channel.userId);
});
// Use bounded caching if caching is necessary
const MAX_ENTRIES = 1000;
function safeSet(map, key, value) {
if (map.size >= MAX_ENTRIES) {
const firstKey = map.keys().next().value;
map.delete(firstKey);
}
map.set(key, value);
}
Additionally, validate and restrict claims size in token generation to minimize per-request memory impact. Combine these practices with periodic heap profiling to detect residual retention patterns. middleBrick’s CLI can be used in scripts to verify remediation outcomes by scanning endpoints for insecure token handling patterns and ensuring findings related to authentication state are addressed.