Overview
Unrestricted Resource Consumption vulnerabilities in Node.js Express allow attackers to exhaust CPU, memory, or file descriptors by sending oversized requests or triggering CPU-intensive work in the main thread. In production, this can cause significant latency, degraded service, and outages as requests pile up and the event loop stalls. If memory pressure becomes critical, the process may crash or be terminated, affecting other tenants in shared environments and requiring incident response. Note: No CVE IDs are provided for this guide.
In Express, this class of vulnerability often arises when body parsers buffer entire payloads without strict limits (or when routes perform expensive synchronous computations on user input) and when request handling lacks rate limiting or timeouts. Without these protections, an attacker can sustain load that overwhelms a single Node.js process, affecting all routes and services that share the process.
Remediation patterns include enforcing strict input size limits, applying rate limiting, and moving CPU-bound work to worker threads; using timeouts; streaming or chunked processing; and implementing observability to detect abnormal usage early.
Code Fix Example
Node.js (Express) API Security Remediation
/* Vulnerable version (demonstrates the issue) */
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
// Vulnerable: no body size limit, CPU-heavy work in-request
app.use('/vulnerable', bodyParser.json()); // no limit
app.post('/vulnerable/process', (req, res) => {
const input = req.body;
let sum = 0;
for (let i = 0; i < 1e8; i++) sum += i * (input.factor || 1);
res.json({ ok: true, sum });
});
// Fixed version: limit payload, rate limit, and offload CPU work
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({ windowMs: 60 * 1000, max: 60 });
app.use('/fixed', bodyParser.json({ limit: '100kb' })); // limit payload size
app.use('/fixed', limiter);
const { Worker } = require('worker_threads');
app.post('/fixed/process', (req, res) => {
const workerCode = `
const { parentPort } = require('worker_threads');
parentPort.on('message', (factor) => {
let sum = 0;
for (let i = 0; i < 1e8; i++) sum += i * (factor || 1);
parentPort.postMessage(sum);
});
`;
const worker = new Worker(workerCode, { eval: true });
worker.on('message', (sum) => res.json({ ok: true, sum }));
worker.on('error', () => res.status(500).send('Worker error'));
worker.postMessage(req.body.factor || 1);
});
app.listen(3000, () => console.log('Server running on port 3000'));