-
Notifications
You must be signed in to change notification settings - Fork 28.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
deps,http: http_parser set max header size to 8KB
CVE-2018-12121 PR-URL: nodejs-private/node-private#143 Ref: nodejs-private/security#139 Ref: nodejs-private/http-parser-private#2 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rod Vagg <rod@vagg.org> Reviewed-By: Anna Henningsen <anna@addaleax.net>
- Loading branch information
Showing
4 changed files
with
162 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
'use strict'; | ||
|
||
const assert = require('assert'); | ||
const common = require('../common'); | ||
const http = require('http'); | ||
const net = require('net'); | ||
const MAX = 8 * 1024; // 8KB | ||
|
||
// Verify that we cannot receive more than 8KB of headers. | ||
|
||
function once(cb) { | ||
let called = false; | ||
return () => { | ||
if (!called) { | ||
called = true; | ||
cb(); | ||
} | ||
}; | ||
} | ||
|
||
function finished(client, callback) { | ||
'abort error end'.split(' ').forEach((e) => { | ||
client.on(e, once(() => setImmediate(callback))); | ||
}); | ||
} | ||
|
||
function fillHeaders(headers, currentSize, valid = false) { | ||
// Generate valid headers | ||
if (valid) { | ||
// TODO(mcollina): understand why -9 is needed instead of -1 | ||
headers = headers.slice(0, -9); | ||
} | ||
return headers + '\r\n\r\n'; | ||
} | ||
|
||
const timeout = common.platformTimeout(10); | ||
|
||
function writeHeaders(socket, headers) { | ||
const array = []; | ||
|
||
// this is off from 1024 so that \r\n does not get split | ||
const chunkSize = 1000; | ||
let last = 0; | ||
|
||
for (let i = 0; i < headers.length / chunkSize; i++) { | ||
const current = (i + 1) * chunkSize; | ||
array.push(headers.slice(last, current)); | ||
last = current; | ||
} | ||
|
||
// safety check we are chunking correctly | ||
assert.strictEqual(array.join(''), headers); | ||
|
||
next(); | ||
|
||
function next() { | ||
if (socket.write(array.shift())) { | ||
if (array.length === 0) { | ||
socket.end(); | ||
} else { | ||
setTimeout(next, timeout); | ||
} | ||
} else { | ||
socket.once('drain', next); | ||
} | ||
} | ||
} | ||
|
||
function test1() { | ||
let headers = | ||
'HTTP/1.1 200 OK\r\n' + | ||
'Content-Length: 0\r\n' + | ||
'X-CRASH: '; | ||
|
||
// OK, Content-Length, 0, X-CRASH, aaa... | ||
const currentSize = 2 + 14 + 1 + 7; | ||
headers = fillHeaders(headers, currentSize); | ||
|
||
const server = net.createServer((sock) => { | ||
sock.once('data', (chunk) => { | ||
writeHeaders(sock, headers); | ||
sock.resume(); | ||
}); | ||
}); | ||
|
||
server.listen(0, common.mustCall(() => { | ||
const port = server.address().port; | ||
const client = http.get({ port: port }, common.mustNotCall(() => {})); | ||
|
||
client.on('error', common.mustCall((err) => { | ||
assert.strictEqual(err.code, 'HPE_HEADER_OVERFLOW'); | ||
server.close(); | ||
setImmediate(test2); | ||
})); | ||
})); | ||
} | ||
|
||
const test2 = common.mustCall(() => { | ||
let headers = | ||
'GET / HTTP/1.1\r\n' + | ||
'Host: localhost\r\n' + | ||
'Agent: node\r\n' + | ||
'X-CRASH: '; | ||
|
||
// /, Host, localhost, Agent, node, X-CRASH, a... | ||
const currentSize = 1 + 4 + 9 + 5 + 4 + 7; | ||
headers = fillHeaders(headers, currentSize); | ||
|
||
const server = http.createServer(common.mustNotCall()); | ||
|
||
server.on('clientError', common.mustCall((err) => { | ||
assert.strictEqual(err.code, 'HPE_HEADER_OVERFLOW'); | ||
})); | ||
|
||
server.listen(0, common.mustCall(() => { | ||
const client = net.connect(server.address().port); | ||
client.on('connect', () => { | ||
writeHeaders(client, headers); | ||
client.resume(); | ||
}); | ||
|
||
finished(client, common.mustCall((err) => { | ||
server.close(); | ||
setImmediate(test3); | ||
})); | ||
})); | ||
}); | ||
|
||
const test3 = common.mustCall(() => { | ||
let headers = | ||
'GET / HTTP/1.1\r\n' + | ||
'Host: localhost\r\n' + | ||
'Agent: node\r\n' + | ||
'X-CRASH: '; | ||
|
||
// /, Host, localhost, Agent, node, X-CRASH, a... | ||
const currentSize = 1 + 4 + 9 + 5 + 4 + 7; | ||
headers = fillHeaders(headers, currentSize, true); | ||
|
||
const server = http.createServer(common.mustCall((req, res) => { | ||
res.end('hello world'); | ||
setImmediate(server.close.bind(server)); | ||
})); | ||
|
||
server.listen(0, common.mustCall(() => { | ||
const client = net.connect(server.address().port); | ||
client.on('connect', () => { | ||
writeHeaders(client, headers); | ||
client.resume(); | ||
}); | ||
})); | ||
}); | ||
|
||
test1(); |
74e01d0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apologies,
--max-http-header-size
is already added: 693e362175The comment here https://github.com/nodejs/node/blob/master/deps/http_parser/http_parser.c#L141 indicates that it's the total size of the headers including status line that is limited to 8K. The maximum of 8K for all the headers seems unreasonably low.This adversely affects systems with upstream load balancers and web servers before node that have higher limits. E.g. AWS ALB has 64K limit for all headers https://docs.aws.amazon.com/elasticloadbalancing/latest/userguide/how-elastic-load-balancing-works.html and 16K for a single line.Please consider allowing to opt-out of this limit or implementing a more granular check (e.g. no more than 1000 concurrent requests over 8k limit are allowed).