Unrestricted Resource Consumption

Unrestricted Resource Consumption in Node.js (Express) [GHSA-qj83-cq47-w5f8]

[Updated month year] Updated GHSA-qj83-cq47-w5f8

Overview

Unrestricted Resource Consumption vulnerabilities in Node.js and Express allow an attacker to exhaust server CPU, memory, or I/O by triggering heavy processing or parsing large inputs. In practice, a single request that performs a long-running computation or consumes large in-memory payloads can starve the event loop, delay or drop other requests, and potentially crash the process if memory is exhausted. This risk is exacerbated in apps that perform synchronous work in routes, parse large JSON bodies, or accept large file uploads without enforcing limits. In Express apps, unbounded processing may happen when routes perform CPU-bound loops, regexes, or data transformation on unchecked inputs, or when body parsers read the entire payload into memory. Attackers can send many concurrent requests or oversized payloads, causing cascading latency, degraded service, and outages. Mitigation must start at the boundary, with input limits and non-blocking processing. Remediation focuses on preventing unbounded work: apply strict request size limits (body parsers, upload middleware), stream or chunk processing, offload CPU-heavy tasks to worker threads or separate services, implement rate limiting, timeouts, and budgets, and enforce resource quotas in containers. Combine code fixes with monitoring, testing, and defensive configurations to reduce blast radius.

Code Fix Example

Node.js (Express) API Security Remediation
// Vulnerable pattern
const express = require('express');
const app = express();
app.post('/process', (req, res) => {
  // CPU-intensive, unbounded work on request
  let sum = 0;
  for (let i = 0; i < 1e9; i++) sum += i; // CPU-heavy loop
  res.send('done');
});
app.listen(3000, () => console.log('listening'));

// Fixed using worker threads and basic limits
const { Worker } = require('worker_threads');
const express2 = require('express');
const app2 = express2();
// Basic limit: you would typically apply body parser limits in real apps
app2.post('/process', (req, res) => {
  const worker = new Worker('./heavy.js');
  worker.once('message', (msg) => res.send(msg));
  worker.once('error', () => res.status(500).send('error'));
  worker.postMessage(null);
});
app2.listen(3001, () => console.log('listening on 3001'));

// heavy.js (worker file)
const { parentPort } = require('worker_threads');
let sum = 0;
for (let i = 0; i < 1e9; i++) sum += i;
parentPort.postMessage('done');

CVE References

Choose which optional cookies to allow. You can change this any time.