Memory Leak with Bearer Tokens
How Memory Leak Manifests in Bearer Tokens
Memory leaks in Bearer Token implementations occur when authentication tokens persist in memory longer than necessary, creating attack surfaces for credential theft. The most common pattern involves caching entire HTTP responses containing Bearer Tokens without proper cleanup mechanisms.
Consider this problematic Node.js Express middleware:
const tokenCache = [];
function authMiddleware(req, res, next) {
const token = req.headers.authorization;
tokenCache.push(token); // Memory leak: tokens never removed
next();
}
app.use(authMiddleware);
app.get('/api/data', (req, res) => {
res.json({ data: 'sensitive information' });
});The tokenCache array grows indefinitely as requests arrive, holding references to every Bearer Token ever processed. In a production system handling thousands of requests per minute, this creates a significant memory footprint.
Another manifestation appears in WebSocket implementations where tokens are stored in closure scopes:
const activeConnections = new Map();
function createWebSocketHandler(token) {
return (ws) => {
activeConnections.set(ws, { token, timestamp: Date.now() });
ws.on('message', (data) => {
// Process message
});
ws.on('close', () => {
activeConnections.delete(ws); // Missing: token cleanup
});
};
}
app.ws('/ws', createWebSocketHandler(req.headers.authorization));The Map retains token references even after connections close, preventing garbage collection. Attackers exploiting memory dumps could extract valid tokens from these structures.
HTTP server implementations also leak tokens through improper response object handling:
const responseCache = new Map();
app.get('/api/resource', (req, res) => {
const token = req.headers.authorization;
const data = fetchResource();
const response = {
statusCode: 200,
headers: res.getHeaders(),
body: JSON.stringify(data)
};
responseCache.set(Date.now(), response); // Memory leak: caching entire response
res.json(data);
});Response objects contain headers with Authorization fields, creating circular references that prevent memory cleanup. Over time, this cache grows without bounds, potentially exposing cached tokens to unauthorized processes.
Bearer Tokens-Specific Detection
Detecting memory leaks in Bearer Token implementations requires analyzing both code patterns and runtime behavior. Static analysis tools can identify problematic code structures:
import { ESLint } from 'eslint';
const eslint = new ESLint({
useEslintrc: true,
ignore: false
});
async function detectTokenLeaks(filePath) {
const results = await eslint.lintFiles(filePath);
const issues = results.flatMap(result =>
result.messages.filter(msg =>
msg.ruleId === 'no-unnecessary-caching' ||
msg.ruleId === 'no-memory-leak'
)
);
return issues;
}
Dynamic analysis involves monitoring memory usage patterns during authentication operations. A simple Node.js memory profiler:
const { performance } = require('perf_hooks');
const memwatch = require('memwatch-next');
class TokenMemoryMonitor {
constructor() {
this.baseline = process.memoryUsage().heapUsed;
this.maxGrowth = 0;
this.leakDetected = false;
memwatch.on('stats', (stats) => {
const current = process.memoryUsage().heapUsed;
const growth = current - this.baseline;
if (growth > this.maxGrowth) {
this.maxGrowth = growth;
}
if (growth > 100 * 1024 * 1024) { // 100MB growth
this.leakDetected = true;
console.warn('Potential token memory leak detected');
}
});
}
async testLeakScenario(testFunction) {
const start = performance.now();
await testFunction();
const duration = performance.now() - start;
return {
duration,
memoryUsed: process.memoryUsage().heapUsed,
leakDetected: this.leakDetected
};
}
}
Runtime monitoring should track token lifecycle events:
class TokenLifecycleMonitor {
constructor() {
this.activeTokens = new WeakSet();
this.tokenCreationTimes = new Map();
this.maxTokenAge = 30 * 60 * 1000; // 30 minutes
}
trackToken(token) {
this.activeTokens.add(token);
this.tokenCreationTimes.set(token, Date.now());
}
untrackToken(token) {
this.activeTokens.delete(token);
this.tokenCreationTimes.delete(token);
}
checkForStaleTokens() {
const now = Date.now();
for (const [token, createdAt] of this.tokenCreationTimes) {
if (now - createdAt > this.maxTokenAge) {
console.warn(`Stale token detected: ${token.substring(0, 20)}...`);
this.untrackToken(token);
}
}
}
}
Automated scanning tools like middleBrick can identify these patterns across your API surface. The platform tests for memory leak vulnerabilities by:
- Analyzing response headers for Authorization fields that persist across requests
- Checking for improper token caching in middleware chains
- Detecting WebSocket implementations that retain token references
- Scanning for static arrays or maps that accumulate tokens without cleanup
middleBrick's black-box scanning approach tests the actual runtime behavior without requiring source code access, making it ideal for identifying memory leaks in production Bearer Token implementations.
Bearer Tokens-Specific Remediation
Remediating memory leaks in Bearer Token implementations requires systematic cleanup of token references and proper lifecycle management. The most effective approach uses WeakMap structures that allow garbage collection when tokens are no longer needed:
class SecureTokenManager {
constructor() {
this.tokenStore = new WeakMap();
this.tokenExpiry = new Map();
this.cleanupInterval = null;
}
initializeCleanup() {
this.cleanupInterval = setInterval(() => {
this.cleanupExpiredTokens();
}, 5 * 60 * 1000); // Every 5 minutes
}
storeToken(token, metadata) {
this.tokenStore.set(token, metadata);
this.tokenExpiry.set(token, Date.now() + metadata.ttl);
}
retrieveToken(token) {
return this.tokenStore.get(token);
}
cleanupExpiredTokens() {
const now = Date.now();
for (const [token, expiry] of this.tokenExpiry) {
if (now > expiry) {
this.tokenStore.delete(token);
this.tokenExpiry.delete(token);
}
}
}
shutdown() {
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
}
}
}
Middleware implementations should use scoped token storage rather than global caches:
function secureAuthMiddleware(req, res, next) {
const token = req.headers.authorization;
// Store token in request-scoped context only
req.tokenContext = {
token,
timestamp: Date.now(),
isValid: validateToken(token)
};
// Ensure cleanup on response finish
res.on('finish', () => {
delete req.tokenContext;
});
next();
}
function validateToken(token) {
// Validate without storing references
return token && token.startsWith('Bearer ') && token.length > 10;
}
WebSocket handlers require explicit cleanup in close event handlers:
class SecureWebSocketHandler {
constructor(token) {
this.token = token;
this.active = true;
this.metadata = null;
}
onMessage(data) {
// Process message without token retention
processData(data, this.token);
}
onClose() {
this.active = false;
// Remove all references to enable garbage collection
this.token = null;
this.metadata = null;
}
destroy() {
// Explicit cleanup for immediate memory release
this.onClose();
Object.keys(this).forEach(key => delete this[key]);
}
}
Response caching should exclude Authorization headers and use weak references:
class SecureResponseCache {
constructor() {
this.cache = new WeakMap();
this.strongCache = new Map(); // For non-sensitive data only
this.ttl = 60 * 1000; // 1 minute
}
storeResponse(req, res) {
const key = req.originalUrl;
const responseData = {
statusCode: res.statusCode,
headers: this.sanitizeHeaders(res.getHeaders()),
body: res.body
};
this.strongCache.set(key, {
data: responseData,
timestamp: Date.now()
});
}
sanitizeHeaders(headers) {
const sanitized = { ...headers };
delete sanitized.authorization;
delete sanitized.cookie;
return sanitized;
}
cleanup() {
const now = Date.now();
for (const [key, entry] of this.strongCache) {
if (now - entry.timestamp > this.ttl) {
this.strongCache.delete(key);
}
}
}
}
Continuous monitoring with middleBrick helps verify remediation effectiveness. The platform's scanning capabilities test for:
- Proper token cleanup in middleware chains
- Memory usage patterns during authentication flows
- WebSocket implementations with proper token lifecycle management
- Response caching mechanisms that exclude sensitive headers
middleBrick's GitHub Action integration allows automated security testing in CI/CD pipelines, ensuring memory leak vulnerabilities are caught before deployment. The platform provides specific remediation guidance for Bearer Token implementations, helping developers fix issues efficiently.
Frequently Asked Questions
How do I know if my Bearer Token implementation has memory leaks?
--inspect flag with Chrome DevTools, or implement custom monitoring that tracks token lifecycle events. middleBrick can automatically detect common memory leak patterns in Bearer Token implementations through its black-box scanning approach.