Overview
Unrestricted Resource Consumption vulnerabilities allow attackers to exhaust server resources by sending large or crafted payloads or by triggering CPU-heavy work without limits. In real-world outages, such abuse can cause memory pressure, thread starvation, and service unavailability. In Node.js with Express, this class of vulnerability often arises when request bodies are buffered entirely in userland code or when CPU-intensive processing is performed inside the single-threaded event loop. Without input limits, rate limiting, or offloading work, a single client can saturate RAM and CPU, forcing timeouts and degraded performance. Remediation combines strict input limits, streaming processing, and task isolation. Enforce payload size limits at the framework layer, avoid buffering large payloads in application code, and move heavy computation to worker threads or separate services. Add rate limiting, timeouts, and robust monitoring to detect abnormal patterns early. This guide provides a concrete vulnerable pattern and a safe alternative for Node.js (Express) projects, plus concrete steps to prevent recurrence across APIs and microservices.
Code Fix Example
Node.js (Express) API Security Remediation
/* Vulnerable pattern and fixed pattern in one file (Node.js/Express) */
const express = require('express');
const { Worker } = require('worker_threads');
const app = express();
// Vulnerable route: reads body into memory and performs CPU work on the main thread
app.post('/process/vuln', (req, res) => {
let chunks = [];
req.on('data', (chunk) => chunks.push(chunk));
req.on('end', () => {
const buf = Buffer.concat(chunks);
// CPU-intensive operation on the main thread
let sum = 0;
for (let i = 0; i < 1e7; i++) sum += i;
res.json({ size: buf.length, sum });
});
});
// Fixed route: enforce payload size limits and offload CPU work to a worker thread
app.use('/process/fix', express.json({ limit: '100kb' }));
app.post('/process/fix', async (req, res) => {
const body = req.body;
try {
const result = await runWorker(body);
res.json(result);
} catch (err) {
res.status(500).json({ error: 'Internal error' });
}
});
function runWorker(data) {
return new Promise((resolve, reject) => {
const workerCode = `
const { parentPort } = require('worker_threads');
parentPort.on('message', (payload) => {
// Simulate CPU-intensive work
let s = 0;
for (let i = 0; i < 1e7; i++) s = (s + i) % 1000000;
parentPort.postMessage({ ok: true, result: s, input: payload });
});
`;
const worker = new Worker(workerCode, { eval: true });
worker.on('message', (msg) => resolve(msg));
worker.on('error', reject);
worker.postMessage(data);
});
}
app.listen(3000, () => console.log('Server listening on port 3000'));